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, QQuickWindow *window)
65 QQuickCanvasPixmap::QQuickCanvasPixmap(QQuickPixmap *pixmap, QQuickWindow *window)
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_window);
110 m_texture = QQuickWindowPrivate::get(m_window)->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()
206 \instantiates QQuickCanvasItem
207 \inqmlmodule QtQuick 2
210 \ingroup qtquick-canvas
211 \ingroup qtquick-visual
212 \brief Provides a 2D canvas item enabling drawing via JavaScript
214 The Canvas item allows drawing of straight and curved lines, simple and
215 complex shapes, graphs, and referenced graphic images. It can also add
216 text, colors, shadows, gradients, and patterns, and do low level pixel
217 operations. The Canvas output may be saved as an image file or serialized
220 To define a drawing area in the Canvas item set the \c width and \c height
221 properties. For example, the following code creates a Canvas item which
222 has a drawing area with a height of 100 pixels and width of 200 pixels:
232 Currently the Canvas item only supports the two-dimensional rendering context.
234 \section1 Threaded Rendering and Render Target
236 The Canvas item supports two render targets: \c Canvas.Image and
237 \c Canvas.FramebufferObject.
239 The \c Canvas.Image render target is a \a QImage object. This render
240 target supports background thread rendering, allowing complex or long
241 running painting to be executed without blocking the UI.
243 The Canvas.FramebufferObject render target utilizes OpenGL hardware
244 acceleration rather than rendering into system memory, which in many cases
245 results in faster rendering.
247 The default render target is Canvas.Image and the default renderStrategy is
250 \section1 Tiled Canvas
251 The Canvas item supports tiled rendering by setting \l canvasSize, \l tileSize
252 and \l canvasWindow properties.
254 Tiling allows efficient display of a very large virtual canvas via a smaller
255 canvas window. The actual memory consumption is in relation to the canvas
256 window size. The painting code can draw within the virtual canvas without
257 handling coordinate system transformations.
259 The tiles overlapping with the canvas window may be cached eliminating the
260 need to redraw, which can lead to significantly improved performance in
263 \section1 Pixel Operations
264 All HTML5 2D context pixel operations are supported. In order to ensure
265 improved pixel reading/writing performance the \a Canvas.Image render
266 target should be chosen. The \a Canvas.FramebufferObject render target
267 requires the pixel data to be exchanged between the system memory and the
268 graphic card, which is significantly more expensive. Rendering may also be
269 synchronized with the V-sync signal (to avoid
270 \l{en.wikipedia.org/wiki/Screen_tearing}{screen tearing}) which will further
271 impact pixel operations with \c Canvas.FrambufferObject render target.
273 \section1 Tips for Porting Existing HTML5 Canvas applications
275 Although the Canvas item is provides a HTML5 like API, HTML5 canvas
276 applications need to be modified to run in the Canvas item:
278 \li Replace all DOM API calls with QML property bindings or Canvas item methods.
279 \li Replace all HTML event handlers with the MouseArea item.
280 \li Change setInterval/setTimeout function calls with the \l Timer item or
281 the use of requestAnimationFrame().
282 \li Place painting code into the onPaint handler and trigger
283 painting by calling the markDirty() or requestPaint() methods.
284 \li To draw images, load them by calling the Canvas's loadImage() method and then request to paint
285 them in the onImageLoaded handler.
291 QQuickCanvasItem::QQuickCanvasItem(QQuickItem *parent)
292 : QQuickItem(*(new QQuickCanvasItemPrivate), parent)
294 setFlag(ItemHasContents);
297 QQuickCanvasItem::~QQuickCanvasItem()
299 Q_D(QQuickCanvasItem);
304 \qmlproperty size QtQuick2::Canvas::available
306 Indicates when Canvas is able to provide a drawing context to operate on.
309 bool QQuickCanvasItem::isAvailable() const
311 return d_func()->available;
315 \qmlproperty string QtQuick2::Canvas::contextType
316 The type of drawing context to use.
318 This property is set to the name of the active context type.
320 If set explicitly the canvas will attempt to create a context of the
321 named type after becoming available.
323 The type name is the same as used in the getContext() call, for the 2d
324 canvas the value will be "2d".
326 \sa getContext(), available
329 QString QQuickCanvasItem::contextType() const
331 return d_func()->contextType;
334 void QQuickCanvasItem::setContextType(const QString &contextType)
336 Q_D(QQuickCanvasItem);
338 if (contextType.compare(d->contextType, Qt::CaseInsensitive) == 0)
341 if (d->contextInitialized) {
342 qmlInfo(this) << "Canvas already initialized with a different context type";
346 d->contextType = contextType;
349 createContext(contextType);
351 emit contextTypeChanged();
355 \qmlproperty object QtQuick2::Canvas::context
356 Holds the active drawing context.
358 If the canvas is ready and there has been a successful call to getContext()
359 or the contextType property has been set with a supported context type,
360 this property will contain the current drawing context, otherwise null.
363 QQmlV8Handle QQuickCanvasItem::context() const
365 Q_D(const QQuickCanvasItem);
366 if (d->contextInitialized)
367 return QQmlV8Handle::fromHandle(d->context->v8value());
369 return QQmlV8Handle::fromHandle(v8::Null());
373 \qmlproperty size QtQuick2::Canvas::canvasSize
374 Holds the logical canvas size that the context paints on.
376 By default, the canvas size is the same size as the current canvas item
379 By setting the canvasSize, tileSize and canvasWindow, the Canvas item can
380 act as a large virtual canvas with many separately rendered tile rectangles.
381 Only those tiles within the current canvas window are painted by the Canvas
384 \sa tileSize, canvasWindow
386 QSizeF QQuickCanvasItem::canvasSize() const
388 Q_D(const QQuickCanvasItem);
389 return d->canvasSize;
392 void QQuickCanvasItem::setCanvasSize(const QSizeF & size)
394 Q_D(QQuickCanvasItem);
395 if (d->canvasSize != size) {
396 d->hasCanvasSize = true;
397 d->canvasSize = size;
398 emit canvasSizeChanged();
400 if (d->contextInitialized)
406 \qmlproperty size QtQuick2::Canvas::tileSize
407 Holds the canvas rendering tile size.
409 The Canvas item enters tiled mode by setting canvasSize, tileSize and the
410 canvasWindow. This can improve rendering performance by rendering and
411 caching tiles instead of rendering the whole canvas every time.
413 Memory will be consumed only by those tiles within the current visible
416 By default the tileSize is the same as the canvasSize.
418 \sa canvasSize, canvasWindow
420 QSize QQuickCanvasItem::tileSize() const
422 Q_D(const QQuickCanvasItem);
426 void QQuickCanvasItem::setTileSize(const QSize & size)
428 Q_D(QQuickCanvasItem);
429 if (d->tileSize != size) {
430 d->hasTileSize = true;
433 emit tileSizeChanged();
435 if (d->contextInitialized)
441 \qmlproperty rect QtQuick2::Canvas::canvasWindow
442 Holds the current canvas visible window.
444 By default the canvasWindow size is the same as the Canvas item size with
445 the top-left point as (0, 0).
447 If the canvasSize is different to the Canvas item size, the Canvas item
448 can display different visible areas by changing the canvas windowSize
451 \sa canvasSize, tileSize
453 QRectF QQuickCanvasItem::canvasWindow() const
455 Q_D(const QQuickCanvasItem);
456 return d->canvasWindow;
459 void QQuickCanvasItem::setCanvasWindow(const QRectF& rect)
461 Q_D(QQuickCanvasItem);
462 if (d->canvasWindow != rect) {
463 d->canvasWindow = rect;
465 d->hasCanvasWindow = true;
466 emit canvasWindowChanged();
468 if (d->contextInitialized)
474 \qmlproperty enumeration QtQuick2::Canvas::renderTarget
475 Holds the current canvas render target.
478 \li Canvas.Image - render to an in memory image buffer.
479 \li Canvas.FramebufferObject - render to an OpenGL frame buffer
482 This hint is supplied along with renderStrategy to the graphics context to
483 determine the method of rendering. A renderStrategy, renderTarget or a
484 combination may not be supported by a graphics context, in which case the
485 context will choose appropriate options and Canvas will signal the change
488 The default render target is \c Canvas.FramebufferObject.
490 QQuickCanvasItem::RenderTarget QQuickCanvasItem::renderTarget() const
492 Q_D(const QQuickCanvasItem);
493 return d->renderTarget;
496 void QQuickCanvasItem::setRenderTarget(QQuickCanvasItem::RenderTarget target)
498 Q_D(QQuickCanvasItem);
499 if (d->renderTarget != target) {
500 if (d->contextInitialized) {
501 qmlInfo(this) << "Canvas:renderTarget not changeble once context is active.";
505 d->renderTarget = target;
506 emit renderTargetChanged();
511 \qmlproperty enumeration QtQuick2::Canvas::renderStrategy
512 Holds the current canvas rendering strategy.
515 \li Canvas.Immediate - context will perform graphics commands immediately in the main UI thread.
516 \li Canvas.Threaded - context will defer graphics commands to a private rendering thread.
517 \li Canvas.Cooperative - context will defer graphics commands to the applications global render thread.
520 This hint is supplied along with renderTarget to the graphics context to
521 determine the method of rendering. A renderStrategy, renderTarget or a
522 combination may not be supported by a graphics context, in which case the
523 context will choose appropriate options and Canvas will signal the change
526 Configuration or runtime tests may cause the QML Scene Graph to render in
527 the GUI thread. Selecting \c Canvas.Cooperative, does not guarantee
528 rendering will occur on a thread separate from the GUI thread.
530 The default value is \c Canvas.Cooperative.
535 QQuickCanvasItem::RenderStrategy QQuickCanvasItem::renderStrategy() const
537 return d_func()->renderStrategy;
540 void QQuickCanvasItem::setRenderStrategy(QQuickCanvasItem::RenderStrategy strategy)
542 Q_D(QQuickCanvasItem);
543 if (d->renderStrategy != strategy) {
544 if (d->contextInitialized) {
545 qmlInfo(this) << "Canvas:renderStrategy not changeable once context is active.";
548 d->renderStrategy = strategy;
549 emit renderStrategyChanged();
553 QQuickCanvasContext* QQuickCanvasItem::rawContext() const
555 return d_func()->context;
558 bool QQuickCanvasItem::isPaintConnected()
560 IS_SIGNAL_CONNECTED(this, QQuickCanvasItem, paint, (const QRect &));
563 void QQuickCanvasItem::sceneGraphInitialized()
565 Q_D(QQuickCanvasItem);
568 connect(this, SIGNAL(visibleChanged()), SLOT(checkAnimationCallbacks()));
569 QMetaObject::invokeMethod(this, "availableChanged", Qt::QueuedConnection);
571 if (!d->contextType.isNull())
572 QMetaObject::invokeMethod(this, "delayedCreate", Qt::QueuedConnection);
573 else if (isPaintConnected())
574 QMetaObject::invokeMethod(this, "requestPaint", Qt::QueuedConnection);
577 void QQuickCanvasItem::geometryChanged(const QRectF &newGeometry, const QRectF &oldGeometry)
579 Q_D(QQuickCanvasItem);
581 QQuickItem::geometryChanged(newGeometry, oldGeometry);
583 QSizeF newSize = newGeometry.size();
584 if (!d->hasCanvasSize && d->canvasSize != newSize) {
585 d->canvasSize = newSize;
586 emit canvasSizeChanged();
589 if (!d->hasTileSize && d->tileSize != newSize) {
590 d->tileSize = newSize.toSize();
591 emit tileSizeChanged();
594 const QRectF rect = QRectF(QPointF(0, 0), newSize);
596 if (!d->hasCanvasWindow && d->canvasWindow != rect) {
597 d->canvasWindow = rect;
598 emit canvasWindowChanged();
605 void QQuickCanvasItem::releaseResources()
607 Q_D(QQuickCanvasItem);
615 void QQuickCanvasItem::componentComplete()
617 QQuickItem::componentComplete();
619 Q_D(QQuickCanvasItem);
620 d->baseUrl = qmlEngine(this)->contextForObject(this)->baseUrl();
623 void QQuickCanvasItem::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
625 QQuickItem::itemChange(change, value);
626 if (change != QQuickItem::ItemSceneChange)
629 Q_D(QQuickCanvasItem);
633 if (value.window== 0)
636 d->window = value.window;
637 if (d->window->openglContext() != 0) // available context == initialized
638 sceneGraphInitialized();
640 connect(d->window, SIGNAL(sceneGraphInitialized()), SLOT(sceneGraphInitialized()));
643 void QQuickCanvasItem::updatePolish()
645 QQuickItem::updatePolish();
647 Q_D(QQuickCanvasItem);
649 if (d->contextInitialized)
650 d->context->prepare(d->canvasSize.toSize(), d->tileSize, d->canvasWindow.toRect(), d->dirtyRect.toRect(), d->smooth);
652 if (d->animationCallbacks.size() > 0 && isVisible()) {
653 QMap<int, v8::Persistent<v8::Function> > animationCallbacks = d->animationCallbacks;
654 d->animationCallbacks.clear();
656 foreach (int key, animationCallbacks.keys()) {
657 v8::HandleScope handle_scope;
658 v8::Handle<v8::Object> self = QQmlEnginePrivate::getV8Engine(qmlEngine(this))->newQObject(this).As<v8::Object>();
659 v8::Handle<v8::Value> args[] = { v8::Uint32::New(QDateTime::currentDateTimeUtc().toTime_t()) };
660 v8::Persistent<v8::Function> f = animationCallbacks.value(key);
661 f->Call(self, 1, args);
666 if (d->dirtyRect.isValid()) {
667 if (d->hasTileSize && d->hasCanvasWindow)
668 emit paint(tiledRect(d->canvasWindow.intersected(d->dirtyRect.toAlignedRect()), d->tileSize));
670 emit paint(d->dirtyRect.toRect());
671 d->dirtyRect = QRectF();
675 if (d->contextInitialized) {
676 if (d->renderStrategy == QQuickCanvasItem::Cooperative)
683 QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
685 Q_D(QQuickCanvasItem);
687 if (!d->contextInitialized)
690 QSGSimpleTextureNode *node = static_cast<QSGSimpleTextureNode*>(oldNode);
692 node = new QSGSimpleTextureNode;
694 if (d->renderStrategy == QQuickCanvasItem::Cooperative)
697 node->setTexture(d->context->texture());
698 node->setRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
703 \qmlmethod object QtQuick2::Canvas::getContext(string contextId, any... args)
705 Returns a drawing context or null if no context available.
707 The \a contextId parameter names the required context. The Canvas item
708 will return a context that implements the required drawing mode. After the
709 first call to getContext any subsequent call to getContext with the same
710 contextId will return the same context object.
712 If the context type is not supported or the canvas has previously been
713 requested to provide a different and incompatible context type, null will
716 Canvas only supports a 2d context.
719 void QQuickCanvasItem::getContext(QQmlV8Function *args)
721 Q_D(QQuickCanvasItem);
723 if (args->Length() < 1 || !(*args)[0]->IsString()) {
724 qmlInfo(this) << "getContext should be called with a string naming the required context type";
725 args->returnValue(v8::Null());
730 qmlInfo(this) << "Unable to use getContext() at this time, please wait for available: true";
731 args->returnValue(v8::Null());
735 QString contextId = QString::fromUtf16(*v8::String::Value((*args)[0]));
737 if (d->context != 0) {
738 if (d->context->contextNames().contains(contextId, Qt::CaseInsensitive)) {
739 args->returnValue(d->context->v8value());
743 qmlInfo(this) << "Canvas already initialized with a different context type";
744 args->returnValue(v8::Null());
748 if (createContext(contextId))
749 args->returnValue(d->context->v8value());
751 args->returnValue(v8::Null());
755 \qmlmethod long QtQuick2::Canvas::requestAnimationFrame(callback)
757 This function schedules callback to be invoked before composing the QtQuick
761 void QQuickCanvasItem::requestAnimationFrame(QQmlV8Function *args)
763 if (args->Length() < 1 || !(*args)[0]->IsFunction()) {
764 qmlInfo(this) << "requestAnimationFrame should be called with an animation callback function";
765 args->returnValue(v8::Null());
769 Q_D(QQuickCanvasItem);
773 d->animationCallbacks.insert(++id, v8::Persistent<v8::Function>::New(((*args)[0]).As<v8::Function>()));
778 args->returnValue(v8::Int32::New(id));
782 \qmlmethod QtQuick2::Canvas::cancelRequestAnimationFrame(long handle)
784 This function will cancel the animation callback referenced by \a handle.
787 void QQuickCanvasItem::cancelRequestAnimationFrame(QQmlV8Function *args)
789 if (args->Length() < 1 || !(*args)[0]->IsInt32()) {
790 qmlInfo(this) << "cancelRequestAnimationFrame should be called with an animation callback id";
791 args->returnValue(v8::Null());
795 d_func()->animationCallbacks.remove((*args)[0]->Int32Value());
800 \qmlmethod QtQuick2::Canvas::requestPaint()
802 Request the entire visible region be re-drawn.
807 void QQuickCanvasItem::requestPaint()
809 markDirty(d_func()->canvasWindow);
813 \qmlmethod QtQuick2::Canvas::markDirty(rect area)
815 Mark the given \a area as dirty, so that when this area is visible the
816 canvas renderer will redraw it. This will trigger the onPaint signal
819 \sa paint, requestPaint()
822 void QQuickCanvasItem::markDirty(const QRectF& rect)
824 Q_D(QQuickCanvasItem);
828 d->dirtyRect |= rect;
833 void QQuickCanvasItem::checkAnimationCallbacks()
835 if (d_func()->animationCallbacks.size() > 0 && isVisible())
840 \qmlmethod bool QtQuick2::Canvas::save(string filename)
842 Save the current canvas content into an image file \a filename.
843 The saved image format is automatically decided by the \a filename's
846 Note: calling this method will force painting the whole canvas, not just the
847 current canvas visible window.
849 \sa canvasWindow, canvasSize, toDataURL()
851 bool QQuickCanvasItem::save(const QString &filename) const
853 Q_D(const QQuickCanvasItem);
854 QUrl url = d->baseUrl.resolved(QUrl::fromLocalFile(filename));
855 return toImage().save(url.toLocalFile());
858 QQmlRefPointer<QQuickCanvasPixmap> QQuickCanvasItem::loadedPixmap(const QUrl& url)
860 Q_D(QQuickCanvasItem);
861 QUrl fullPathUrl = d->baseUrl.resolved(url);
862 if (!d->pixmaps.contains(fullPathUrl)) {
865 return d->pixmaps.value(fullPathUrl);
869 \qmlsignal QtQuick2::Canvas::onImageLoaded()
871 This handler is called when an image has been loaded.
877 \qmlmethod QtQuick2::Canvas::loadImage(url image)
878 Loads the given \c image asynchronously.
880 When the image is ready, onImageLoaded will be emitted.
881 The loaded image can be unloaded by the unloadImage() method.
883 Note: Only loaded images can be painted on the Canvas item.
884 \sa unloadImage, onImageLoaded, isImageLoaded(),
885 Context2D::createImageData(), Context2D::drawImage()
887 void QQuickCanvasItem::loadImage(const QUrl& url)
889 Q_D(QQuickCanvasItem);
890 QUrl fullPathUrl = d->baseUrl.resolved(url);
891 if (!d->pixmaps.contains(fullPathUrl)) {
892 QQuickPixmap* pix = new QQuickPixmap();
893 QQmlRefPointer<QQuickCanvasPixmap> canvasPix;
894 canvasPix.take(new QQuickCanvasPixmap(pix, d->window));
895 d->pixmaps.insert(fullPathUrl, canvasPix);
897 pix->load(qmlEngine(this)
899 , QQuickPixmap::Cache | QQuickPixmap::Asynchronous);
900 if (pix->isLoading())
901 pix->connectFinished(this, SIGNAL(imageLoaded()));
905 \qmlmethod QtQuick2::Canvas::unloadImage(url image)
906 Unloads the \c image.
908 Once an image is unloaded it cannot be painted by the canvas context
909 unless it is loaded again.
911 \sa loadImage(), onImageLoaded, isImageLoaded(),
912 Context2D::createImageData(), Context2D::drawImage
914 void QQuickCanvasItem::unloadImage(const QUrl& url)
916 Q_D(QQuickCanvasItem);
917 d->pixmaps.remove(d->baseUrl.resolved(url));
921 \qmlmethod QtQuick2::Canvas::isImageError(url image)
922 Returns true if the \a image failed to load.
926 bool QQuickCanvasItem::isImageError(const QUrl& url) const
928 Q_D(const QQuickCanvasItem);
929 QUrl fullPathUrl = d->baseUrl.resolved(url);
930 return d->pixmaps.contains(fullPathUrl)
931 && d->pixmaps.value(fullPathUrl)->pixmap()->isError();
935 \qmlmethod QtQuick2::Canvas::isImageLoading(url image)
936 Returns true if the \a image is currently loading.
940 bool QQuickCanvasItem::isImageLoading(const QUrl& url) const
942 Q_D(const QQuickCanvasItem);
943 QUrl fullPathUrl = d->baseUrl.resolved(url);
944 return d->pixmaps.contains(fullPathUrl)
945 && d->pixmaps.value(fullPathUrl)->pixmap()->isLoading();
948 \qmlmethod QtQuick2::Canvas::isImageLoaded(url image)
949 Returns true if the \a image is sucessfully loaded and ready to use.
953 bool QQuickCanvasItem::isImageLoaded(const QUrl& url) const
955 Q_D(const QQuickCanvasItem);
956 QUrl fullPathUrl = d->baseUrl.resolved(url);
957 return d->pixmaps.contains(fullPathUrl)
958 && d->pixmaps.value(fullPathUrl)->pixmap()->isReady();
961 QImage QQuickCanvasItem::toImage(const QRectF& rect) const
963 Q_D(const QQuickCanvasItem);
964 if (d->contextInitialized) {
966 return d->context->toImage(canvasWindow());
968 return d->context->toImage(rect);
975 \qmlmethod string QtQuick2::Canvas::toDataURL(string mimeType)
977 Returns a data URL for the image in the canvas.
979 The default \a mimeType is "image/png".
983 QString QQuickCanvasItem::toDataURL(const QString& mimeType) const
985 QImage image = toImage();
987 if (!image.isNull()) {
990 buffer.open(QIODevice::WriteOnly);
991 QString mime = mimeType.toLower();
993 if (mime == QLatin1String("image/png")) {
994 type = QStringLiteral("PNG");
995 } else if (mime == QLatin1String("image/bmp"))
996 type = QStringLiteral("BMP");
997 else if (mime == QLatin1String("image/jpeg"))
998 type = QStringLiteral("JPEG");
999 else if (mime == QLatin1String("image/x-portable-pixmap"))
1000 type = QStringLiteral("PPM");
1001 else if (mime == QLatin1String("image/tiff"))
1002 type = QStringLiteral("TIFF");
1003 else if (mime == QLatin1String("image/xpm"))
1004 type = QStringLiteral("XPM");
1006 return QStringLiteral("data:,");
1008 image.save(&buffer, type.toLatin1());
1010 QString dataUrl = QStringLiteral("data:%1;base64,%2");
1011 return dataUrl.arg(mime).arg(QLatin1String(ba.toBase64().constData()));
1013 return QStringLiteral("data:,");
1016 void QQuickCanvasItem::delayedCreate()
1018 Q_D(QQuickCanvasItem);
1020 if (!d->contextInitialized && !d->contextType.isNull())
1021 createContext(d->contextType);
1026 bool QQuickCanvasItem::createContext(const QString &contextType)
1028 Q_D(QQuickCanvasItem);
1030 if (contextType == QLatin1String("2d")) {
1031 if (d->contextType.compare(QLatin1String("2d"), Qt::CaseInsensitive) != 0) {
1032 d->contextType = QLatin1String("2d");
1033 emit contextTypeChanged(); // XXX: can't be in setContextType()
1035 initializeContext(new QQuickContext2D(this));
1042 void QQuickCanvasItem::initializeContext(QQuickCanvasContext *context, const QVariantMap &args)
1044 Q_D(QQuickCanvasItem);
1046 d->context = context;
1047 d->context->init(this, args);
1048 d->context->setV8Engine(QQmlEnginePrivate::getV8Engine(qmlEngine(this)));
1049 d->contextInitialized = true;
1050 connect(d->context, SIGNAL(textureChanged()), SLOT(update()));
1051 connect(d->context, SIGNAL(textureChanged()), SIGNAL(painted()));
1052 emit contextChanged();
1055 QRect QQuickCanvasItem::tiledRect(const QRectF &window, const QSize &tileSize)
1057 if (window.isEmpty())
1060 const int tw = tileSize.width();
1061 const int th = tileSize.height();
1062 const int h1 = window.left() / tw;
1063 const int v1 = window.top() / th;
1065 const int htiles = ((window.right() - h1 * tw) + tw - 1)/tw;
1066 const int vtiles = ((window.bottom() - v1 * th) + th - 1)/th;
1068 return QRect(h1 * tw, v1 * th, htiles * tw, vtiles * th);
1072 \qmlsignal QtQuick2::Canvas::onPaint(rect region)
1074 This handler is called to render the \a region. If a context is active it
1075 can be referenced from the context property.
1077 This signal can be triggered markdirty(), requestPaint() or by changing
1078 the current canvas window.
1082 \qmlsignal QtQuick2::Canvas::onPainted()
1084 This handler is called after all context painting commands are executed and
1085 the Canvas has been rendered.