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
209 \ingroup qtquick-canvas
210 \ingroup qtquick-visual
211 \brief Provides a 2D canvas item enabling drawing via JavaScript
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
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:
231 Currently the Canvas item only supports the two-dimensional rendering context.
233 \section1 Threaded Rendering and Render Target
235 The Canvas item supports two render targets: \c Canvas.Image and
236 \c Canvas.FramebufferObject.
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.
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.
246 The default render target is Canvas.Image and the default renderStrategy is
249 \section1 Tiled Canvas
250 The Canvas item supports tiled rendering by setting \l canvasSize, \l tileSize
251 and \l canvasWindow properties.
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.
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
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.
272 \section1 Tips for Porting Existing HTML5 Canvas applications
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:
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.
287 \sa QtQuick2::Context2D
290 QQuickCanvasItem::QQuickCanvasItem(QQuickItem *parent)
291 : QQuickItem(*(new QQuickCanvasItemPrivate), parent)
293 setFlag(ItemHasContents);
296 QQuickCanvasItem::~QQuickCanvasItem()
298 Q_D(QQuickCanvasItem);
303 \qmlproperty size QtQuick2::Canvas::available
305 Indicates when Canvas is able to provide a drawing context to operate on.
308 bool QQuickCanvasItem::isAvailable() const
310 return d_func()->available;
314 \qmlproperty string QtQuick2::Canvas::contextType
315 The type of drawing context to use.
317 This property is set to the name of the active context type.
319 If set explicitly the canvas will attempt to create a context of the
320 named type after becoming available.
322 The type name is the same as used in the getContext() call, for the 2d
323 canvas the value will be "2d".
325 \sa QtQuick2::Canvas::getContext, QtQuick2::Canvas::available
328 QString QQuickCanvasItem::contextType() const
330 return d_func()->contextType;
333 void QQuickCanvasItem::setContextType(const QString &contextType)
335 Q_D(QQuickCanvasItem);
337 if (contextType.compare(d->contextType, Qt::CaseInsensitive) == 0)
340 if (d->contextInitialized) {
341 qmlInfo(this) << "Canvas already initialized with a different context type";
345 d->contextType = contextType;
348 createContext(contextType);
350 emit contextTypeChanged();
354 \qmlproperty object QtQuick2::Canvas::context
355 Holds the active drawing context.
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.
362 QQmlV8Handle QQuickCanvasItem::context() const
364 Q_D(const QQuickCanvasItem);
365 if (d->contextInitialized)
366 return QQmlV8Handle::fromHandle(d->context->v8value());
368 return QQmlV8Handle::fromHandle(v8::Null());
372 \qmlproperty size QtQuick2::Canvas::canvasSize
373 Holds the logical canvas size that the context paints on.
375 By default, the canvas size is the same size as the current canvas item
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
383 \sa QtQuick2::Canvas::tileSize, QtQuick2::Canvas::canvasWindow
385 QSizeF QQuickCanvasItem::canvasSize() const
387 Q_D(const QQuickCanvasItem);
388 return d->canvasSize;
391 void QQuickCanvasItem::setCanvasSize(const QSizeF & size)
393 Q_D(QQuickCanvasItem);
394 if (d->canvasSize != size) {
395 d->hasCanvasSize = true;
396 d->canvasSize = size;
397 emit canvasSizeChanged();
399 if (d->contextInitialized)
405 \qmlproperty size QtQuick2::Canvas::tileSize
406 Holds the canvas rendering tile size.
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.
412 Memory will be consumed only by those tiles within the current visible
415 By default the tileSize is the same as the canvasSize.
417 \sa QtQuick2::Canvas::canvaasSize, QtQuick2::Canvas::canvasWindow
419 QSize QQuickCanvasItem::tileSize() const
421 Q_D(const QQuickCanvasItem);
425 void QQuickCanvasItem::setTileSize(const QSize & size)
427 Q_D(QQuickCanvasItem);
428 if (d->tileSize != size) {
429 d->hasTileSize = true;
432 emit tileSizeChanged();
434 if (d->contextInitialized)
440 \qmlproperty rect QtQuick2::Canvas::canvasWindow
441 Holds the current canvas visible window.
443 By default the canvasWindow size is the same as the Canvas item size with
444 the top-left point as (0, 0).
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
450 \sa QtQuick2::Canvas::canvasSize, QtQuick2::Canvas::tileSize
452 QRectF QQuickCanvasItem::canvasWindow() const
454 Q_D(const QQuickCanvasItem);
455 return d->canvasWindow;
458 void QQuickCanvasItem::setCanvasWindow(const QRectF& rect)
460 Q_D(QQuickCanvasItem);
461 if (d->canvasWindow != rect) {
462 d->canvasWindow = rect;
464 d->hasCanvasWindow = true;
465 emit canvasWindowChanged();
467 if (d->contextInitialized)
473 \qmlproperty enumeration QtQuick2::Canvas::renderTarget
474 Holds the current canvas render target.
477 \li Canvas.Image - render to an in memory image buffer.
478 \li Canvas.FramebufferObject - render to an OpenGL frame buffer
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
487 The default render target is \c Canvas.FramebufferObject.
489 QQuickCanvasItem::RenderTarget QQuickCanvasItem::renderTarget() const
491 Q_D(const QQuickCanvasItem);
492 return d->renderTarget;
495 void QQuickCanvasItem::setRenderTarget(QQuickCanvasItem::RenderTarget target)
497 Q_D(QQuickCanvasItem);
498 if (d->renderTarget != target) {
499 if (d->contextInitialized) {
500 qmlInfo(this) << "Canvas:renderTarget not changeble once context is active.";
504 d->renderTarget = target;
505 emit renderTargetChanged();
510 \qmlproperty enumeration QtQuick2::Canvas::renderStrategy
511 Holds the current canvas rendering strategy.
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.
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
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.
529 The default value is \c Canvas.Cooperative.
531 \sa QtQuick2::Canvas::renderTarget
534 QQuickCanvasItem::RenderStrategy QQuickCanvasItem::renderStrategy() const
536 return d_func()->renderStrategy;
539 void QQuickCanvasItem::setRenderStrategy(QQuickCanvasItem::RenderStrategy strategy)
541 Q_D(QQuickCanvasItem);
542 if (d->renderStrategy != strategy) {
543 if (d->contextInitialized) {
544 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::componentComplete()
607 QQuickItem::componentComplete();
609 Q_D(QQuickCanvasItem);
610 d->baseUrl = qmlEngine(this)->contextForObject(this)->baseUrl();
613 void QQuickCanvasItem::itemChange(QQuickItem::ItemChange change, const QQuickItem::ItemChangeData &value)
615 QQuickItem::itemChange(change, value);
616 if (change != QQuickItem::ItemSceneChange)
619 Q_D(QQuickCanvasItem);
623 if (value.canvas == 0)
626 d->canvas = value.canvas;
627 if (d->canvas->openglContext() != 0) // available context == initialized
628 sceneGraphInitialized();
630 connect(d->canvas, SIGNAL(sceneGraphInitialized()), SLOT(sceneGraphInitialized()));
633 void QQuickCanvasItem::updatePolish()
635 QQuickItem::updatePolish();
637 Q_D(QQuickCanvasItem);
639 if (d->contextInitialized)
640 d->context->prepare(d->canvasSize.toSize(), d->tileSize, d->canvasWindow.toRect(), d->dirtyRect.toRect(), d->smooth);
642 if (d->animationCallbacks.size() > 0 && isVisible()) {
643 QMap<int, v8::Persistent<v8::Function> > animationCallbacks = d->animationCallbacks;
644 d->animationCallbacks.clear();
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);
656 if (d->dirtyRect.isValid()) {
657 if (d->hasTileSize && d->hasCanvasWindow)
658 emit paint(tiledRect(d->canvasWindow.intersected(d->dirtyRect.toAlignedRect()), d->tileSize));
660 emit paint(d->dirtyRect.toRect());
661 d->dirtyRect = QRectF();
665 if (d->contextInitialized) {
666 if (d->renderStrategy == QQuickCanvasItem::Cooperative)
673 QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
675 Q_D(QQuickCanvasItem);
677 if (!d->contextInitialized)
680 class CanvasTextureNode : public QSGSimpleTextureNode
683 CanvasTextureNode() : QSGSimpleTextureNode() {}
684 ~CanvasTextureNode() {delete texture();}
687 CanvasTextureNode *node = static_cast<CanvasTextureNode*>(oldNode);
689 node = new CanvasTextureNode;
692 if (d->renderStrategy == QQuickCanvasItem::Cooperative)
695 node->setTexture(d->context->texture());
696 node->setRect(QRectF(QPoint(0, 0), d->canvasWindow.size()));
701 \qmlmethod object QtQuick2::Canvas::getContext(string contextId, any... args)
703 Returns a drawing context or null if no context available.
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.
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
714 Canvas only supports a 2d context.
717 void QQuickCanvasItem::getContext(QQmlV8Function *args)
719 Q_D(QQuickCanvasItem);
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());
728 qmlInfo(this) << "Unable to use getContext() at this time, please wait for available: true";
729 args->returnValue(v8::Null());
733 QString contextId = QString::fromUtf16(*v8::String::Value((*args)[0]));
735 if (d->context != 0) {
736 if (d->context->contextNames().contains(contextId, Qt::CaseInsensitive)) {
737 args->returnValue(d->context->v8value());
741 qmlInfo(this) << "Canvas already initialized with a different context type";
742 args->returnValue(v8::Null());
746 if (createContext(contextId))
747 args->returnValue(d->context->v8value());
749 args->returnValue(v8::Null());
753 \qmlmethod long QtQuick2::Canvas::requestAnimationFrame(callback)
755 This function schedules callback to be invoked before composing the QtQuick
759 void QQuickCanvasItem::requestAnimationFrame(QQmlV8Function *args)
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());
767 Q_D(QQuickCanvasItem);
771 d->animationCallbacks.insert(++id, v8::Persistent<v8::Function>::New(((*args)[0]).As<v8::Function>()));
776 args->returnValue(v8::Int32::New(id));
780 \qmlmethod void QtQuick2::Canvas::cancelRequestAnimationFrmae(long handle)
782 This function will cancel the animation callback referenced by \a handle.
785 void QQuickCanvasItem::cancelRequestAnimationFrame(QQmlV8Function *args)
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());
793 d_func()->animationCallbacks.remove((*args)[0]->Int32Value());
798 \qmlmethod void QtQuick2::Canvas::requestPaint()
800 Request the entire visible region be re-drawn.
802 \sa QtQuick::Canvas::markDirty
805 void QQuickCanvasItem::requestPaint()
807 markDirty(d_func()->canvasWindow);
811 \qmlmethod void QtQuick2::Canvas::markDirty(rect area)
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
817 \sa QtQuick2::Canvas::paint, QtQuick2::Canvas::requestPaint
820 void QQuickCanvasItem::markDirty(const QRectF& rect)
822 Q_D(QQuickCanvasItem);
826 d->dirtyRect |= rect;
831 void QQuickCanvasItem::checkAnimationCallbacks()
833 if (d_func()->animationCallbacks.size() > 0 && isVisible())
838 \qmlmethod bool QtQuick2::Canvas::save(string filename)
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
844 Note: calling this method will force painting the whole canvas, not just the
845 current canvas visible window.
847 \sa canvasWindow, canvasSize, toDataURL
849 bool QQuickCanvasItem::save(const QString &filename) const
851 Q_D(const QQuickCanvasItem);
852 QUrl url = d->baseUrl.resolved(QUrl::fromLocalFile(filename));
853 return toImage().save(url.toLocalFile());
856 QQmlRefPointer<QQuickCanvasPixmap> QQuickCanvasItem::loadedPixmap(const QUrl& url)
858 Q_D(QQuickCanvasItem);
859 QUrl fullPathUrl = d->baseUrl.resolved(url);
860 if (!d->pixmaps.contains(fullPathUrl)) {
863 return d->pixmaps.value(fullPathUrl);
867 \qmlmethod void QtQuick2::Canvas::loadImage(url image)
868 Loads the given \c image asynchronously.
870 When the image is ready, onImageLoaded will be emitted.
871 The loaded image can be unloaded by the \a QtQuick2::Canvas::unloadImage method.
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
877 void QQuickCanvasItem::loadImage(const QUrl& url)
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);
887 pix->load(qmlEngine(this)
889 , QQuickPixmap::Cache | QQuickPixmap::Asynchronous);
890 if (pix->isLoading())
891 pix->connectFinished(this, SIGNAL(imageLoaded()));
895 \qmlmethod void QtQuick2::Canvas::unloadImage(url image)
896 Unloads the \c image.
898 Once an image is unloaded it cannot be painted by the canvas context
899 unless it is loaded again.
901 \sa QtQuick2::Canvas::loadImage, QtQuick2::Canvas::imageLoaded, QtQuick2::Canvas::isImageLoaded,
902 QtQuick2::Context2D::createImageData, QtQuick2::Context2D::drawImage
904 void QQuickCanvasItem::unloadImage(const QUrl& url)
906 Q_D(QQuickCanvasItem);
907 d->pixmaps.remove(d->baseUrl.resolved(url));
911 \qmlmethod void QtQuick2::Canvas::isImageError(url image)
912 Returns true if the \a image failed to load.
914 \sa QtQuick2::Canvas::loadImage
916 bool QQuickCanvasItem::isImageError(const QUrl& url) const
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();
925 \qmlmethod void QtQuick2::Canvas::isImageLoading(url image)
926 Returns true if the \a image is currently loading.
928 \sa QtQuick2::Canvas::loadImage
930 bool QQuickCanvasItem::isImageLoading(const QUrl& url) const
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();
938 \qmlmethod void QtQuick2::Canvas::isImageLoaded(url image)
939 Returns true if the \a image is sucessfully loaded and ready to use.
941 \sa QtQuick2::Canvas::loadImage
943 bool QQuickCanvasItem::isImageLoaded(const QUrl& url) const
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();
951 QImage QQuickCanvasItem::toImage(const QRectF& rect) const
953 Q_D(const QQuickCanvasItem);
954 if (d->contextInitialized) {
956 return d->context->toImage(canvasWindow());
958 return d->context->toImage(rect);
965 \qmlmethod string QtQuick2::Canvas::toDataURL(string mimeType)
967 Returns a data URL for the image in the canvas.
969 The default \a mimeType is "image/png".
971 \sa QtQuick2::Canvas::save
973 QString QQuickCanvasItem::toDataURL(const QString& mimeType) const
975 QImage image = toImage();
977 if (!image.isNull()) {
980 buffer.open(QIODevice::WriteOnly);
981 QString mime = mimeType.toLower();
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");
996 return QStringLiteral("data:,");
998 image.save(&buffer, type.toLatin1());
1000 QString dataUrl = QStringLiteral("data:%1;base64,%2");
1001 return dataUrl.arg(mime).arg(QLatin1String(ba.toBase64().constData()));
1003 return QStringLiteral("data:,");
1006 void QQuickCanvasItem::delayedCreate()
1008 Q_D(QQuickCanvasItem);
1010 if (!d->contextInitialized && !d->contextType.isNull())
1011 createContext(d->contextType);
1016 bool QQuickCanvasItem::createContext(const QString &contextType)
1018 Q_D(QQuickCanvasItem);
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()
1025 initializeContext(new QQuickContext2D(this));
1032 void QQuickCanvasItem::initializeContext(QQuickCanvasContext *context, const QVariantMap &args)
1034 Q_D(QQuickCanvasItem);
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();
1045 QRect QQuickCanvasItem::tiledRect(const QRectF &window, const QSize &tileSize)
1047 if (window.isEmpty())
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;
1055 const int htiles = ((window.right() - h1 * tw) + tw - 1)/tw;
1056 const int vtiles = ((window.bottom() - v1 * th) + th - 1)/th;
1058 return QRect(h1 * tw, v1 * th, htiles * tw, vtiles * th);
1062 \qmlsignal QtQuick2::Canvas::onPaint(rect region)
1064 This handler is called to render the \a region. If a context is active it
1065 can be referenced from the context property.
1067 This signal can be triggered by QtQuick2::Canvas::markdirty,
1068 QtQuick2::Canvas::requestPaint or by changing the current canvas window.
1072 \qmlsignal QtQuick2::Canvas::onPainted()
1074 This handler is called after all context painting commands are executed and
1075 the Canvas has been rendered.