aboutsummaryrefslogtreecommitdiffstats
path: root/src/quick/items/context2d
diff options
context:
space:
mode:
Diffstat (limited to 'src/quick/items/context2d')
-rw-r--r--src/quick/items/context2d/context2d.pri16
-rw-r--r--src/quick/items/context2d/qquickcanvasitem.cpp714
-rw-r--r--src/quick/items/context2d/qquickcanvasitem_p.h148
-rw-r--r--src/quick/items/context2d/qquickcontext2d.cpp3540
-rw-r--r--src/quick/items/context2d/qquickcontext2d_p.h210
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp469
-rw-r--r--src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h266
-rw-r--r--src/quick/items/context2d/qquickcontext2dnode.cpp124
-rw-r--r--src/quick/items/context2d/qquickcontext2dnode_p.h84
-rw-r--r--src/quick/items/context2d/qquickcontext2dtexture.cpp778
-rw-r--r--src/quick/items/context2d/qquickcontext2dtexture_p.h203
-rw-r--r--src/quick/items/context2d/qquickcontext2dtile.cpp165
-rw-r--r--src/quick/items/context2d/qquickcontext2dtile_p.h110
13 files changed, 6827 insertions, 0 deletions
diff --git a/src/quick/items/context2d/context2d.pri b/src/quick/items/context2d/context2d.pri
new file mode 100644
index 0000000000..60b3e4b0c1
--- /dev/null
+++ b/src/quick/items/context2d/context2d.pri
@@ -0,0 +1,16 @@
+SOURCES += \
+ $$PWD/qquickcanvasitem.cpp \
+ $$PWD/qquickcontext2d.cpp \
+ $$PWD/qquickcontext2dnode.cpp \
+ $$PWD/qquickcontext2dtile.cpp \
+ $$PWD/qquickcontext2dtexture.cpp \
+ $$PWD/qquickcontext2dcommandbuffer.cpp \
+
+HEADERS += \
+ $$PWD/qquickcanvasitem_p.h \
+ $$PWD/qquickcontext2d_p.h \
+ $$PWD/qquickcontext2dnode_p.h \
+ $$PWD/qquickcontext2dtile_p.h \
+ $$PWD/qquickcontext2dtexture_p.h \
+ $$PWD/qquickcontext2dcommandbuffer_p.h \
+
diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp
new file mode 100644
index 0000000000..32809babed
--- /dev/null
+++ b/src/quick/items/context2d/qquickcanvasitem.cpp
@@ -0,0 +1,714 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include <private/qsgadaptationlayer_p.h>
+#include "qquickcanvasitem_p.h"
+#include <private/qquickitem_p.h>
+#include "qquickcontext2d_p.h"
+#include "qquickcontext2dnode_p.h"
+#include "qquickcontext2dtexture_p.h"
+#include <QtQuick/private/qdeclarativepixmapcache_p.h>
+
+#include <qdeclarativeinfo.h>
+#include <private/qdeclarativeengine_p.h>
+#include <QtCore/QBuffer>
+
+QT_BEGIN_NAMESPACE
+
+class QQuickCanvasItemPrivate : public QQuickItemPrivate
+{
+public:
+ QQuickCanvasItemPrivate();
+ ~QQuickCanvasItemPrivate();
+ QQuickContext2D* context;
+ QQuickContext2DTexture* texture;
+ QSizeF canvasSize;
+ QSize tileSize;
+ QRectF canvasWindow;
+ QRectF dirtyRect;
+ uint renderInThread : 1;
+ uint hasCanvasSize :1;
+ uint hasTileSize :1;
+ uint hasCanvasWindow :1;
+ uint componentCompleted :1;
+ QQuickCanvasItem::RenderTarget renderTarget;
+ QHash<QUrl, QDeclarativePixmap*> images;
+ QUrl baseUrl;
+};
+
+QQuickCanvasItemPrivate::QQuickCanvasItemPrivate()
+ : QQuickItemPrivate()
+ , context(0)
+ , texture(0)
+ , canvasSize(1, 1)
+ , tileSize(1, 1)
+ , renderInThread(false)
+ , hasCanvasSize(false)
+ , hasTileSize(false)
+ , hasCanvasWindow(false)
+ , componentCompleted(false)
+ , renderTarget(QQuickCanvasItem::FramebufferObject)
+{
+}
+
+QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate()
+{
+ qDeleteAll(images);
+}
+
+/*!
+ \qmlclass Canvas QQuickCanvasItem
+ \inqmlmodule QtQuick 2
+ \since QtQuick 2.0
+ \brief The Canvas item provides a 2D canvas element which enables drawing via Javascript.
+ \inherits Item
+ \ingroup qml-basic-visual-elements
+
+ The Canvas item allows drawing of straight and curved lines, simple and
+ complex shapes, graphs, and referenced graphic images. It can also add text, colors,
+ shadows, gradients, and patterns, and do low level pixel operations. The Canvas
+ output may be saved as an image file or serialized to a url.
+
+ To define a drawing area in the Canvas item set the \c width and \c height properties.
+ For example, the following code creates a Canvas item which has a drawing area with a height of 100
+ pixels and width of 200 pixels:
+ \qml
+ import QtQuick 2.0
+ Canvas {
+ id:mycanvas
+ width:100
+ height:200
+ }
+ \endqml
+
+ Currently the Canvas item only supports the two-dimensional rendering context.
+
+ \section1 Threaded Rendering and Render Target
+
+ The Canvas item supports two render targets: \c Canvas.Image and \c Canvas.FramebufferObject.
+
+ The \c Canvas.Image render target is a \a QImage object. This render target supports background
+ thread rendering, allowing complex or long running painting to be executed without blocking the UI.
+
+ The Canvas.FramebufferObject render target utilizes OpenGL hardware accelaration rather than rendering into
+ system memory, which in many cases results in faster rendering.
+
+ The default render target is Canvas.Image and the default renderInThread property is
+ false.
+
+ \section1 Tiled Canvas
+ The Canvas item supports tiled rendering by setting \l canvasSize, \l tileSize
+ and \l canvasWindow properties.
+
+ Tiling allows efficient display of a very large virtual via a smaller canvas
+ window. The actual memory consumption is in relatation to the canvas window size. The painting
+ code can draw within the virtual canvas without handling coordinate system transformations.
+
+ The tiles overlapping with the canvas window may be cached eliminating the need to redraw,
+ which can lead to significantly improved performance in some situations.
+
+ \section1 Pixel Operations
+ All HTML5 2D context pixel operations are supported. In order to ensure improved
+ pixel reading/writing performance the \a Canvas.Image render target should be chosen. The
+ \a Canvas.FramebufferObject render target requires the pixel data to be exchanged between
+ the system memory and the graphic card, which is significantly more expensive. Rendering
+ may also be synchronized with the V-sync signal (to avoid {en.wikipedia.org/wiki/Screen_tearing}{screen tearing})
+ which will futher impact pixel operations with \c Canvas.FrambufferObject render target.
+
+ \section1 Tips for Porting Existing HTML5 Canvas applications
+
+ Although the Canvas item is provides a HTML5 like API, HTML5 canvas applications
+ need to be modified to run in the Canvas item:
+ \list
+ \o Replace all DOM API calls with QML property bindings or Canvas item methods.
+ \o Replace all HTML event handlers with the \a MouseArea item.
+ \o Change setInterval/setTimeout function calls with the \a Timer item.
+ \o Place painting code into the \a QtQuick2::Canvas::onPaint handler and trigger
+ painting by calling the \c markDirty or \c requestPaint methods.
+ \o To draw images, load them by calling the Canvas's loadImage method and then request to paint
+ them in the onImageLoaded handler.
+ \endlist
+
+ \sa QtQuick2::Context2D
+*/
+
+QQuickCanvasItem::QQuickCanvasItem(QQuickItem *parent)
+ : QQuickItem(*(new QQuickCanvasItemPrivate), parent)
+{
+ setFlag(ItemHasContents);
+}
+
+QQuickCanvasItem::~QQuickCanvasItem()
+{
+ Q_D(QQuickCanvasItem);
+ delete d->context;
+}
+
+/*!
+ \qmlproperty size QtQuick2::Canvas::canvasSize
+ Holds the logical canvas size that the context paints on.
+
+ By default, the canvas size is the same size as the current canvas item size.
+ By setting the canvasSize, tileSize and canvasWindow, the Canvas
+ item can act as a large virtual canvas with many seperately rendered tile rectangles
+ Only those tiles within the current canvas window are painted by
+ the Canvas render engine.
+
+ \sa QtQuick2::Canvas::tileSize QtQuick2::Canvas::canvasWindow
+*/
+QSizeF QQuickCanvasItem::canvasSize() const
+{
+ Q_D(const QQuickCanvasItem);
+ return d->canvasSize;
+}
+
+void QQuickCanvasItem::setCanvasSize(const QSizeF & size)
+{
+ Q_D(QQuickCanvasItem);
+ if (d->canvasSize != size) {
+ d->hasCanvasSize = true;
+ d->canvasSize = size;
+ emit canvasSizeChanged();
+ polish();
+ update();
+ }
+}
+
+/*!
+ \qmlproperty size QtQuick2::Canvas::tileSize
+ Holds the canvas rendering tile size.
+
+ The Canvas item enters tiled mode by setting canvasSize, tileSize and
+ the canvasWindow. This can improve rendering performance
+ by rendering and caching tiles instead of rendering the whole canvas every time.
+
+ Memory will be consumed only by those tiles within the current visible region.
+
+ By default the tileSize is the same as the canvasSize.
+
+ \sa QtQuick2::Canvas::canvaasSize QtQuick2::Canvas::canvasWindow
+*/
+QSize QQuickCanvasItem::tileSize() const
+{
+ Q_D(const QQuickCanvasItem);
+ return d->tileSize;
+}
+
+void QQuickCanvasItem::setTileSize(const QSize & size)
+{
+ Q_D(QQuickCanvasItem);
+ if (d->tileSize != size) {
+ d->hasTileSize = true;
+ d->tileSize = size;
+
+ emit tileSizeChanged();
+ polish();
+ update();
+ }
+}
+
+/*!
+ \qmlproperty rect QtQuick2::Canvas::canvasWindow
+ Holds the current canvas visible window.
+
+ By default the canvasWindow size is the same as the Canvas item
+ size with the topleft point as (0, 0).
+
+ If the canvasSize is different to the Canvas item size, the Canvas
+ item can display different visible areas by changing the canvas windowSize
+ and/or position.
+
+ \sa QtQuick2::Canvas::canvasSize QtQuick2::Canvas::tileSize
+*/
+QRectF QQuickCanvasItem::canvasWindow() const
+{
+ Q_D(const QQuickCanvasItem);
+ return d->canvasWindow;
+}
+
+void QQuickCanvasItem::setCanvasWindow(const QRectF& rect)
+{
+ Q_D(QQuickCanvasItem);
+ if (d->canvasWindow != rect) {
+ d->canvasWindow = rect;
+
+ d->hasCanvasWindow = true;
+ emit canvasWindowChanged();
+ polish();
+ update();
+ }
+}
+
+
+QQuickContext2D* QQuickCanvasItem::context() const
+{
+ Q_D(const QQuickCanvasItem);
+ return d->context;
+}
+/*!
+ \qmlproperty bool QtQuick2::Canvas::renderInThread
+ Holds the current canvas rendering mode.
+
+ Set renderInThread to true to render complex and long
+ running painting in a dedicated background
+ thread, avoiding blocking the main UI.
+
+ \note: Not all renderTargets support background rendering. If background rendering
+ is not supported by the current renderTarget, the renderInThread
+ property is ignored.
+
+ The default value is false.
+ \sa QtQuick2::Canvas::renderTarget
+*/
+bool QQuickCanvasItem::renderInThread() const
+{
+ Q_D(const QQuickCanvasItem);
+ return d->renderInThread;
+}
+/*!
+ \qmlproperty bool QtQuick2::Canvas::renderTarget
+ Holds the current canvas render target.
+
+ \list
+ \o Canvas.Image - render to an in memory image buffer, the render
+ target supports background rendering.
+ \o Canvas.FramebufferObject - render to an OpenGL frame buffer,
+ this render target will ignore the
+ renderInThread property. The actual
+ rendering happens in the main QML rendering
+ process, which may be in a seperate render thread
+ or in the main GUI thread depending upon the platform.
+ \endlist
+
+ The default render target is \c Canvas.Image.
+ \sa QtQuick2::Canvas::renderInThread
+*/
+QQuickCanvasItem::RenderTarget QQuickCanvasItem::renderTarget() const
+{
+ Q_D(const QQuickCanvasItem);
+ return d->renderTarget;
+}
+
+void QQuickCanvasItem::setRenderTarget(RenderTarget target)
+{
+ Q_D(QQuickCanvasItem);
+ if (d->renderTarget != target) {
+ d->renderTarget = target;
+
+ if (d->componentCompleted)
+ createTexture();
+ emit renderTargetChanged();
+ }
+}
+
+void QQuickCanvasItem::_doPainting(const QRectF& region)
+{
+ Q_D(QQuickCanvasItem);
+ emit paint(QDeclarativeV8Handle::fromHandle(d->context->v8value())
+ , QQuickContext2DTexture::tiledRect(region, d->tileSize));
+ if (d->texture)
+ d->texture->wake();
+}
+
+void QQuickCanvasItem::setRenderInThread(bool renderInThread)
+{
+ Q_D(QQuickCanvasItem);
+ if (d->renderInThread != renderInThread) {
+ d->renderInThread = renderInThread;
+
+ if (d->componentCompleted)
+ createTexture();
+
+ if (d->renderInThread)
+ connect(this, SIGNAL(painted()), SLOT(update()));
+ else
+ disconnect(this, SIGNAL(painted()), this, SLOT(update()));
+ emit renderInThreadChanged();
+ polish();
+ update();
+ }
+}
+
+void QQuickCanvasItem::geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry)
+{
+ Q_D(QQuickCanvasItem);
+ QQuickItem::geometryChanged(newGeometry, oldGeometry);
+
+ const qreal w = newGeometry.width();
+ const qreal h = newGeometry.height();
+
+ if (!d->hasCanvasSize) {
+ d->canvasSize = QSizeF(w, h);
+ emit canvasSizeChanged();
+ }
+
+ if (!d->hasTileSize) {
+ d->tileSize = d->canvasSize.toSize();
+ emit tileSizeChanged();
+ }
+
+ if (!d->hasCanvasWindow) {
+ d->canvasWindow = newGeometry;
+ emit canvasWindowChanged();
+ }
+
+ polish();
+ update();
+}
+
+void QQuickCanvasItem::componentComplete()
+{
+ Q_D(QQuickCanvasItem);
+ QQuickItem::componentComplete();
+
+ if (!d->context)
+ createContext();
+ createTexture();
+
+ d->baseUrl = qmlEngine(this)->contextForObject(this)->baseUrl();
+ requestPaint();
+ updatePolish(); //force update the canvas sizes to texture for the first time
+ update();
+ d->componentCompleted = true;
+}
+
+void QQuickCanvasItem::updatePolish()
+{
+ Q_D(QQuickCanvasItem);
+ QQuickItem::updatePolish();
+ if (d->texture) {
+ if (!d->renderInThread && d->dirtyRect.isValid())
+ _doPainting(d->dirtyRect);
+
+ d->texture->canvasChanged(d->canvasSize.toSize()
+ , d->tileSize
+ , d->canvasWindow.toAlignedRect()
+ , d->dirtyRect.toAlignedRect()
+ , d->smooth);
+ d->dirtyRect = QRectF();
+ }
+}
+
+QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
+{
+ Q_D(QQuickCanvasItem);
+ QQuickContext2DNode *node = static_cast<QQuickContext2DNode *>(oldNode);
+ if (!node)
+ node = new QQuickContext2DNode(this);
+
+ node->setTexture(d->texture);
+ node->setSize(d->canvasWindow.size());
+ node->update();
+ return node;
+}
+
+void QQuickCanvasItem::createTexture()
+{
+ Q_D(QQuickCanvasItem);
+
+ if (!d->texture
+ || d->texture->threadRendering() != d->renderInThread
+ || d->texture->renderTarget() != d->renderTarget) {
+ if (d->texture) {
+ d->texture->deleteLater();
+ d->texture = 0;
+ }
+
+ if (d->renderTarget == QQuickCanvasItem::Image) {
+ d->texture = new QQuickContext2DImageTexture(d->renderInThread);
+ } else if (d->renderTarget == QQuickCanvasItem::FramebufferObject) {
+ d->texture = new QQuickContext2DFBOTexture();
+ }
+
+ if (d->renderInThread && !d->texture->supportThreadRendering()) {
+ qWarning("Canvas: render target does not support thread rendering, force to non-thread rendering mode.");
+ d->renderInThread = false;
+ emit renderInThreadChanged();
+ }
+
+ if (d->renderInThread)
+ connect(d->texture, SIGNAL(textureChanged()), this, SLOT(update()));
+
+ d->texture->setItem(this);
+ }
+}
+
+void QQuickCanvasItem::createContext()
+{
+ Q_D(QQuickCanvasItem);
+
+ delete d->context;
+
+ d->context = new QQuickContext2D(this);
+
+ QV8Engine *e = QDeclarativeEnginePrivate::getV8Engine(qmlEngine(this));
+ d->context->setV8Engine(e);
+}
+
+/*!
+ \qmlmethod object QtQuick2::Canvas::getContext(string contextId)
+
+ Currently, the canvas item only supports the 2D context. If the \a contextId
+ parameter isn't provided or is "2d", then the QtQuick2::Context2D object is
+ returned, otherwise returns an invalid value.
+ */
+QDeclarativeV8Handle QQuickCanvasItem::getContext(const QString &contextId)
+{
+ Q_D(QQuickCanvasItem);
+
+ if (contextId.toLower() != QLatin1String("2d"))
+ return QDeclarativeV8Handle::fromHandle(v8::Undefined());
+
+ if (!d->context)
+ createContext();
+ return QDeclarativeV8Handle::fromHandle(d->context->v8value());
+}
+
+/*!
+ \qmlmethod void QtQuick2::Canvas::markDirty(rect region)
+
+ Mark the given \a region as dirty, so that when this region is visible
+ the canvas renderer will redraw it. This will trigger the "onPaint" signal
+ handler function.
+
+ \sa QtQuick2::Canvas::paint QtQuick2::Canvas::requestPaint
+ */
+void QQuickCanvasItem::markDirty(const QRectF& region)
+{
+ Q_D(QQuickCanvasItem);
+ d->dirtyRect |= region;
+ if (d->componentCompleted)
+ polish();
+ update();
+}
+
+
+/*!
+ \qmlmethod bool QtQuick2::Canvas::save(string filename)
+
+ Save the current canvas content into an image file \a filename.
+ The saved image format is automatically decided by the \a filename's
+ suffix.
+
+ Note: calling this method will force painting the whole canvas, not just the
+ current canvas visible window.
+
+ \sa canvasWindow canvasSize toDataURL
+ */
+bool QQuickCanvasItem::save(const QString &filename) const
+{
+ Q_D(const QQuickCanvasItem);
+ QUrl url = d->baseUrl.resolved(QUrl::fromLocalFile(filename));
+ return toImage().save(url.toLocalFile());
+}
+
+QImage QQuickCanvasItem::loadedImage(const QUrl& url)
+{
+ Q_D(QQuickCanvasItem);
+ QUrl fullPathUrl = d->baseUrl.resolved(url);
+ if (!d->images.contains(fullPathUrl)) {
+ loadImage(url);
+ }
+ QDeclarativePixmap* pix = d->images.value(fullPathUrl);
+ if (pix->isLoading() || pix->isError()) {
+ return QImage();
+ }
+ return pix->image();
+}
+
+/*!
+ \qmlmethod void QtQuick2::Canvas::loadImage(url image)
+ Loads the given \c image asynchronously.
+
+ When the image is ready, onImageLoaded will be emitted.
+ The loaded image can be unloaded by the \a QtQuick2::Canvas::unloadImage method.
+
+ Note: Only loaded images can be painted on the Canvas item.
+ \sa QtQuick2::Canvas::unloadImage QtQuick2::Canvas::imageLoaded QtQuick2::Canvas::isImageLoaded
+ \sa QtQuick2::Context2D::createImageData QtQuick2::Context2D::drawImage
+ */
+void QQuickCanvasItem::loadImage(const QUrl& url)
+{
+ Q_D(QQuickCanvasItem);
+ QUrl fullPathUrl = d->baseUrl.resolved(url);
+ if (!d->images.contains(fullPathUrl)) {
+ QDeclarativePixmap* pix = new QDeclarativePixmap();
+ d->images.insert(fullPathUrl, pix);
+
+ pix->load(qmlEngine(this)
+ , fullPathUrl
+ , QDeclarativePixmap::Cache | QDeclarativePixmap::Asynchronous);
+ pix->connectFinished(this, SIGNAL(imageLoaded()));
+ }
+}
+/*!
+ \qmlmethod void QtQuick2::Canvas::unloadImage(url image)
+ Unloads the \c image.
+
+ Once an image is unloaded it cannot be painted by the canvas context
+ unless it is loaded again.
+
+ \sa QtQuick2::Canvas::loadImage QtQuick2::Canvas::imageLoaded QtQuick2::Canvas::isImageLoaded
+ \sa QtQuick2::Context2D::createImageData QtQuick2::Context2D::drawImage
+ */
+void QQuickCanvasItem::unloadImage(const QUrl& url)
+{
+ Q_D(QQuickCanvasItem);
+ QUrl removeThis = d->baseUrl.resolved(url);
+ if (d->images.contains(removeThis)) {
+ delete d->images.value(removeThis);
+ d->images.remove(removeThis);
+ }
+}
+
+/*!
+ \qmlmethod void QtQuick2::Canvas::isImageError(url image)
+ Returns true if the \a image failed to load.
+
+ \sa QtQuick2::Canvas::loadImage
+ */
+bool QQuickCanvasItem::isImageError(const QUrl& url) const
+{
+ Q_D(const QQuickCanvasItem);
+ QUrl fullPathUrl = d->baseUrl.resolved(url);
+ return d->images.contains(fullPathUrl)
+ && d->images.value(fullPathUrl)->isError();
+}
+
+/*!
+ \qmlmethod void QtQuick2::Canvas::isImageLoading(url image)
+ Returns true if the \a image is currently loading.
+
+ \sa QtQuick2::Canvas::loadImage
+ */
+bool QQuickCanvasItem::isImageLoading(const QUrl& url) const
+{
+ Q_D(const QQuickCanvasItem);
+ QUrl fullPathUrl = d->baseUrl.resolved(url);
+ return d->images.contains(fullPathUrl)
+ && d->images.value(fullPathUrl)->isLoading();
+}
+/*!
+ \qmlmethod void QtQuick2::Canvas::isImageLoaded(url image)
+ Returns true if the \a image is sucessfully loaded and ready to use.
+
+ \sa QtQuick2::Canvas::loadImage
+ */
+bool QQuickCanvasItem::isImageLoaded(const QUrl& url) const
+{
+ Q_D(const QQuickCanvasItem);
+ QUrl fullPathUrl = d->baseUrl.resolved(url);
+ return d->images.contains(fullPathUrl)
+ && d->images.value(fullPathUrl)->isReady();
+}
+
+QImage QQuickCanvasItem::toImage(const QRectF& region) const
+{
+ Q_D(const QQuickCanvasItem);
+ if (d->texture) {
+ if (region.isEmpty())
+ return d->texture->toImage(canvasWindow());
+ else
+ return d->texture->toImage(region);
+ }
+ return QImage();
+}
+
+/*!
+ \qmlmethod string QtQuick2::Canvas::toDataURL(string mimeType)
+
+ Returns a data URL for the image in the canvas.
+
+ The default \a mimeType is "image/png".
+
+ \sa QtQuick2::Canvas::save
+ */
+QString QQuickCanvasItem::toDataURL(const QString& mimeType) const
+{
+ QImage image = toImage();
+
+ if (!image.isNull()) {
+ QByteArray ba;
+ QBuffer buffer(&ba);
+ buffer.open(QIODevice::WriteOnly);
+ QString mime = mimeType.toLower();
+ QString type;
+ if (mime == QLatin1Literal("image/png")) {
+ type = QLatin1Literal("PNG");
+ } else if (mime == QLatin1Literal("image/bmp"))
+ type = QLatin1Literal("BMP");
+ else if (mime == QLatin1Literal("image/jpeg"))
+ type = QLatin1Literal("JPEG");
+ else if (mime == QLatin1Literal("image/x-portable-pixmap"))
+ type = QLatin1Literal("PPM");
+ else if (mime == QLatin1Literal("image/tiff"))
+ type = QLatin1Literal("TIFF");
+ else if (mime == QLatin1Literal("image/xpm"))
+ type = QLatin1Literal("XPM");
+ else
+ return QLatin1Literal("data:,");
+
+ image.save(&buffer, type.toAscii());
+ buffer.close();
+ QString dataUrl = QLatin1Literal("data:%1;base64,%2");
+ return dataUrl.arg(mime).arg(QLatin1String(ba.toBase64().constData()));
+ }
+ return QLatin1Literal("data:,");
+}
+
+/*!
+ \qmlsignal QtQuick2::Canvas::onPaint(QtQuick2::Context2D context, rect region)
+
+ This handler is called to render the \a region.
+
+ This signal can be triggered by QtQuick2::Canvas::markdirty, QtQuick2::Canvas::requestPaint
+ or by changing the current canvas window.
+*/
+
+/*!
+ \qmlsignal QtQuick2::Canvas::onPainted()
+
+ This handler is called after all context painting commands are executed and
+ the Canvas has been rendered.
+*/
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/context2d/qquickcanvasitem_p.h b/src/quick/items/context2d/qquickcanvasitem_p.h
new file mode 100644
index 0000000000..70ca475c96
--- /dev/null
+++ b/src/quick/items/context2d/qquickcanvasitem_p.h
@@ -0,0 +1,148 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKCANVASITEM_P_H
+#define QQUICKCANVASITEM_P_H
+
+#include <QtQuick/qquickitem.h>
+#include <private/qv8engine_p.h>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickContext2D;
+class QQuickCanvasItemPrivate;
+class Q_QUICK_EXPORT QQuickCanvasItem : public QQuickItem
+{
+ Q_OBJECT
+ Q_ENUMS(RenderTarget)
+ Q_ENUMS(ImageFilterMode)
+
+ Q_PROPERTY(QSizeF canvasSize READ canvasSize WRITE setCanvasSize NOTIFY canvasSizeChanged)
+ Q_PROPERTY(QSize tileSize READ tileSize WRITE setTileSize NOTIFY tileSizeChanged)
+ Q_PROPERTY(QRectF canvasWindow READ canvasWindow WRITE setCanvasWindow NOTIFY canvasWindowChanged)
+ Q_PROPERTY(bool renderInThread READ renderInThread WRITE setRenderInThread NOTIFY renderInThreadChanged)
+ Q_PROPERTY(RenderTarget renderTarget READ renderTarget WRITE setRenderTarget NOTIFY renderTargetChanged)
+public:
+ enum RenderTarget {
+ Image,
+ FramebufferObject
+ };
+
+ enum ImageFilterMode {
+ Threshold,
+ Mono,
+ GrayScale,
+ Brightness,
+ Invert,
+ Blur,
+ Opaque,
+ Convolute
+ };
+
+ QQuickCanvasItem(QQuickItem *parent = 0);
+ ~QQuickCanvasItem();
+
+ QSizeF canvasSize() const;
+ void setCanvasSize(const QSizeF &);
+
+ QSize tileSize() const;
+ void setTileSize(const QSize &);
+
+ QRectF canvasWindow() const;
+ void setCanvasWindow(const QRectF& rect);
+
+ bool renderInThread() const;
+ void setRenderInThread(bool renderInThread);
+
+ RenderTarget renderTarget() const;
+ void setRenderTarget(RenderTarget target);
+
+ QQuickContext2D* context() const;
+ QImage toImage(const QRectF& region = QRectF()) const;
+
+ QImage loadedImage(const QUrl& url);
+
+Q_SIGNALS:
+ void paint(QDeclarativeV8Handle context, const QRect &region);
+ void painted();
+ void canvasSizeChanged();
+ void tileSizeChanged();
+ void renderInThreadChanged();
+ void textureChanged();
+ void canvasWindowChanged();
+ void renderTargetChanged();
+ void imageLoaded();
+public Q_SLOTS:
+ QString toDataURL(const QString& type = QLatin1String("image/png")) const;
+ QDeclarativeV8Handle getContext(const QString & = QLatin1String("2d"));
+ void markDirty(const QRectF& region);
+ void requestPaint() {markDirty(canvasWindow());}
+ // Save current canvas to disk
+ bool save(const QString& filename) const;
+ void loadImage(const QUrl& url);
+ void unloadImage(const QUrl& url);
+ bool isImageLoaded(const QUrl& url) const;
+ bool isImageLoading(const QUrl& url) const;
+ bool isImageError(const QUrl& url) const;
+private Q_SLOTS:
+ void _doPainting(const QRectF& region);
+protected:
+ virtual void componentComplete();
+ virtual QSGNode *updatePaintNode(QSGNode *, UpdatePaintNodeData *);
+ virtual void geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry);
+ virtual void updatePolish();
+private:
+ void createContext();
+ void createTexture();
+ Q_DECLARE_PRIVATE(QQuickCanvasItem)
+ friend class QQuickContext2D;
+ friend class QQuickContext2DTexture;
+};
+QT_END_NAMESPACE
+
+QML_DECLARE_TYPE(QQuickCanvasItem)
+
+QT_END_HEADER
+
+#endif //QQUICKCANVASITEM_P_H
diff --git a/src/quick/items/context2d/qquickcontext2d.cpp b/src/quick/items/context2d/qquickcontext2d.cpp
new file mode 100644
index 0000000000..d5d57132bf
--- /dev/null
+++ b/src/quick/items/context2d/qquickcontext2d.cpp
@@ -0,0 +1,3540 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickcontext2d_p.h"
+#include "qquickcontext2dcommandbuffer_p.h"
+#include "qquickcanvasitem_p.h"
+#include <private/qquickitem_p.h>
+#include <QtQuick/private/qquickshadereffectsource_p.h>
+#include <QtGui/qopenglframebufferobject.h>
+
+#include <QtCore/qdebug.h>
+#include <QtQuick/private/qsgcontext_p.h>
+#include <private/qdeclarativesvgparser_p.h>
+#include <private/qdeclarativepath_p.h>
+
+#include <private/qquickimage_p_p.h>
+
+#include <QtGui/qguiapplication.h>
+#include <qdeclarativeinfo.h>
+#include <QtCore/qmath.h>
+#include <private/qv8engine_p.h>
+
+#include <qdeclarativeengine.h>
+#include <private/qv8domerrors_p.h>
+#include <QtCore/qnumeric.h>
+
+QT_BEGIN_NAMESPACE
+/*!
+ \qmlclass Context2D QQuickContext2D
+ \inqmlmodule QtQuick 2
+ \since QtQuick 2.0
+ \brief The Context2D API allows you to draw 2d graphic shapes on the \c Canvas item.
+
+ The Context2D object can be created by \c Canvas item's \c getContext() method:
+ \code
+ Canvas {
+ id:canvas
+ onPaint:{
+ var ctx = canvas.getContext('2d');
+ //...
+ }
+ }
+ \endcode
+ The Context2D API implements the same \l {http://www.w3.org/TR/2dcontext}{W3C Canvas 2D Context API standard}
+ with some enhanced features.
+
+ The Context2D API provides the rendering \bold{context} which defines the methods and attributes needed to draw
+ on the \c Canvas item. The following assigns the canvas rendering context to a \c{context}
+ variable:
+ \code
+ var context = mycanvas.getContext("2d")
+ \endcode
+
+ The Context2D API renders the canvas as a coordinate system whose origin (0,0) is
+ at the top left corner, as shown in the figure below. Coordinates increase along
+ the \c{x} axis from left to right and along the \c{y} axis from top to bottom of
+ the canvas.
+ \image qml-item-canvas-context.gif
+*/
+
+Q_CORE_EXPORT double qstrtod(const char *s00, char const **se, bool *ok);
+
+static const double Q_PI = 3.14159265358979323846; // pi
+
+#define DEGREES(t) ((t) * 180.0 / Q_PI)
+
+#define CHECK_CONTEXT(r) if (!r || !r->context || !r->context->buffer()) \
+ V8THROW_ERROR("Not a Context2D object");
+
+#define CHECK_CONTEXT_SETTER(r) if (!r || !r->context || !r->context->buffer()) \
+ V8THROW_ERROR_SETTER("Not a Context2D object");
+#define qClamp(val, min, max) qMin(qMax(val, min), max)
+#define CHECK_RGBA(c) (c == '-' || c == '.' || (c >=0 && c <= 9))
+QColor qt_color_from_string(v8::Local<v8::Value> name)
+{
+ v8::String::AsciiValue str(name);
+
+ char *p = *str;
+ int len = str.length();
+ //rgb/hsl color string has at least 7 characters
+ if (!p || len > 255 || len <= 7)
+ return QColor(p);
+ else {
+ bool isRgb(false), isHsl(false), hasAlpha(false);
+ Q_UNUSED(isHsl)
+
+ while (isspace(*p)) p++;
+ if (strncmp(p, "rgb", 3) == 0)
+ isRgb = true;
+ else if (strncmp(p, "hsl", 3) == 0)
+ isHsl = true;
+ else
+ return QColor(p);
+
+ p+=3; //skip "rgb" or "hsl"
+ hasAlpha = (*p == 'a') ? true : false;
+
+ ++p; //skip "("
+
+ if (hasAlpha) ++p; //skip "a"
+
+ int rh, gs, bl, alpha = 255;
+
+ //red
+ while (isspace(*p)) p++;
+ rh = strtol(p, &p, 10);
+ if (*p == '%') {
+ rh = qRound(rh/100.0 * 255);
+ ++p;
+ }
+ if (*p++ != ',') return QColor();
+
+ //green
+ while (isspace(*p)) p++;
+ gs = strtol(p, &p, 10);
+ if (*p == '%') {
+ gs = qRound(gs/100.0 * 255);
+ ++p;
+ }
+ if (*p++ != ',') return QColor();
+
+ //blue
+ while (isspace(*p)) p++;
+ bl = strtol(p, &p, 10);
+ if (*p == '%') {
+ bl = qRound(bl/100.0 * 255);
+ ++p;
+ }
+
+ if (hasAlpha) {
+ if (*p++!= ',') return QColor();
+ while (isspace(*p)) p++;
+ bool ok = false;
+ alpha = qRound(qstrtod(p, const_cast<const char **>(&p), &ok) * 255);
+ }
+
+ if (*p != ')') return QColor();
+ if (isRgb)
+ return QColor::fromRgba(qRgba(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255)));
+ else
+ return QColor::fromHsl(qClamp(rh, 0, 255), qClamp(gs, 0, 255), qClamp(bl, 0, 255), qClamp(alpha, 0, 255));
+ }
+ return QColor();
+}
+
+QFont qt_font_from_string(const QString& fontString) {
+ QFont font;
+ // ### this is simplified and incomplete
+ // ### TODO:get code from Qt webkit
+ QStringList tokens = fontString.split(QLatin1String(" "));
+ foreach (const QString &token, tokens) {
+ if (token == QLatin1String("italic"))
+ font.setItalic(true);
+ else if (token == QLatin1String("bold"))
+ font.setBold(true);
+ else if (token.endsWith(QLatin1String("px"))) {
+ QString number = token;
+ number.remove(QLatin1String("px"));
+ //font.setPointSizeF(number.trimmed().toFloat());
+ font.setPixelSize(number.trimmed().toInt());
+ } else
+ font.setFamily(token);
+ }
+
+ return font;
+}
+
+
+
+class QQuickContext2DEngineData : public QV8Engine::Deletable
+{
+public:
+ QQuickContext2DEngineData(QV8Engine *engine);
+ ~QQuickContext2DEngineData();
+
+ v8::Persistent<v8::Function> constructorContext;
+ v8::Persistent<v8::Function> constructorGradient;
+ v8::Persistent<v8::Function> constructorPattern;
+ v8::Persistent<v8::Function> constructorPixelArray;
+ v8::Persistent<v8::Function> constructorImageData;
+};
+
+V8_DEFINE_EXTENSION(QQuickContext2DEngineData, engineData)
+
+class QV8Context2DResource : public QV8ObjectResource
+{
+ V8_RESOURCE_TYPE(Context2DType)
+public:
+ QV8Context2DResource(QV8Engine *e) : QV8ObjectResource(e) {}
+ QQuickContext2D* context;
+};
+
+class QV8Context2DStyleResource : public QV8ObjectResource
+{
+ V8_RESOURCE_TYPE(Context2DStyleType)
+public:
+ QV8Context2DStyleResource(QV8Engine *e)
+ : QV8ObjectResource(e)
+ , patternRepeatX(false)
+ , patternRepeatY(false)
+ {}
+ QBrush brush;
+ bool patternRepeatX:1;
+ bool patternRepeatY:1;
+};
+
+class QV8Context2DPixelArrayResource : public QV8ObjectResource
+{
+ V8_RESOURCE_TYPE(Context2DPixelArrayType)
+public:
+ QV8Context2DPixelArrayResource(QV8Engine *e) : QV8ObjectResource(e) {}
+
+ QImage image;
+};
+
+QImage qt_image_convolute_filter(const QImage& src, const QVector<qreal>& weights, int radius = 0)
+{
+ int sides = radius ? radius : qRound(qSqrt(weights.size()));
+ int half = qFloor(sides/2);
+
+ QImage dst = QImage(src.size(), src.format());
+ int w = src.width();
+ int h = src.height();
+ for (int y = 0; y < dst.height(); ++y) {
+ QRgb *dr = (QRgb*)dst.scanLine(y);
+ for (int x = 0; x < dst.width(); ++x) {
+ unsigned char* dRgb = ((unsigned char*)&dr[x]);
+ unsigned char red=0, green=0, blue=0, alpha=0;
+ int sy = y;
+ int sx = x;
+
+ for (int cy=0; cy<sides; cy++) {
+ for (int cx=0; cx<sides; cx++) {
+ int scy = sy + cy - half;
+ int scx = sx + cx - half;
+ if (scy >= 0 && scy < w && scx >= 0 && scx < h) {
+ const QRgb *sr = (const QRgb*)(src.constScanLine(scy));
+ const unsigned char* sRgb = ((const unsigned char*)&sr[scx]);
+ qreal wt = radius ? weights[0] : weights[cy*sides+cx];
+ red += sRgb[0] * wt;
+ green += sRgb[1] * wt;
+ blue += sRgb[2] * wt;
+ alpha += sRgb[3] * wt;
+ }
+ }
+ }
+ dRgb[0] = red;
+ dRgb[1] = green;
+ dRgb[2] = blue;
+ dRgb[3] = alpha;
+ }
+ }
+ return dst;
+}
+
+void qt_image_boxblur(QImage& image, int radius, bool quality)
+{
+ int passes = quality? 3: 1;
+ for (int i=0; i < passes; i++) {
+ image = qt_image_convolute_filter(image, QVector<qreal>() << 1.0/(radius * radius * 1.0), radius);
+ }
+}
+
+static QPainter::CompositionMode qt_composite_mode_from_string(const QString &compositeOperator)
+{
+ if (compositeOperator == QLatin1String("source-over")) {
+ return QPainter::CompositionMode_SourceOver;
+ } else if (compositeOperator == QLatin1String("source-out")) {
+ return QPainter::CompositionMode_SourceOut;
+ } else if (compositeOperator == QLatin1String("source-in")) {
+ return QPainter::CompositionMode_SourceIn;
+ } else if (compositeOperator == QLatin1String("source-atop")) {
+ return QPainter::CompositionMode_SourceAtop;
+ } else if (compositeOperator == QLatin1String("destination-atop")) {
+ return QPainter::CompositionMode_DestinationAtop;
+ } else if (compositeOperator == QLatin1String("destination-in")) {
+ return QPainter::CompositionMode_DestinationIn;
+ } else if (compositeOperator == QLatin1String("destination-out")) {
+ return QPainter::CompositionMode_DestinationOut;
+ } else if (compositeOperator == QLatin1String("destination-over")) {
+ return QPainter::CompositionMode_DestinationOver;
+ } else if (compositeOperator == QLatin1String("lighter")) {
+ return QPainter::CompositionMode_Lighten;
+ } else if (compositeOperator == QLatin1String("copy")) {
+ return QPainter::CompositionMode_Source;
+ } else if (compositeOperator == QLatin1String("xor")) {
+ return QPainter::CompositionMode_Xor;
+ } else if (compositeOperator == QLatin1String("qt-clear")) {
+ return QPainter::CompositionMode_Clear;
+ } else if (compositeOperator == QLatin1String("qt-destination")) {
+ return QPainter::CompositionMode_Destination;
+ } else if (compositeOperator == QLatin1String("qt-multiply")) {
+ return QPainter::CompositionMode_Multiply;
+ } else if (compositeOperator == QLatin1String("qt-screen")) {
+ return QPainter::CompositionMode_Screen;
+ } else if (compositeOperator == QLatin1String("qt-overlay")) {
+ return QPainter::CompositionMode_Overlay;
+ } else if (compositeOperator == QLatin1String("qt-darken")) {
+ return QPainter::CompositionMode_Darken;
+ } else if (compositeOperator == QLatin1String("qt-lighten")) {
+ return QPainter::CompositionMode_Lighten;
+ } else if (compositeOperator == QLatin1String("qt-color-dodge")) {
+ return QPainter::CompositionMode_ColorDodge;
+ } else if (compositeOperator == QLatin1String("qt-color-burn")) {
+ return QPainter::CompositionMode_ColorBurn;
+ } else if (compositeOperator == QLatin1String("qt-hard-light")) {
+ return QPainter::CompositionMode_HardLight;
+ } else if (compositeOperator == QLatin1String("qt-soft-light")) {
+ return QPainter::CompositionMode_SoftLight;
+ } else if (compositeOperator == QLatin1String("qt-difference")) {
+ return QPainter::CompositionMode_Difference;
+ } else if (compositeOperator == QLatin1String("qt-exclusion")) {
+ return QPainter::CompositionMode_Exclusion;
+ }
+ return QPainter::CompositionMode_SourceOver;
+}
+
+static QString qt_composite_mode_to_string(QPainter::CompositionMode op)
+{
+ switch (op) {
+ case QPainter::CompositionMode_SourceOver:
+ return QLatin1String("source-over");
+ case QPainter::CompositionMode_DestinationOver:
+ return QLatin1String("destination-over");
+ case QPainter::CompositionMode_Clear:
+ return QLatin1String("qt-clear");
+ case QPainter::CompositionMode_Source:
+ return QLatin1String("copy");
+ case QPainter::CompositionMode_Destination:
+ return QLatin1String("qt-destination");
+ case QPainter::CompositionMode_SourceIn:
+ return QLatin1String("source-in");
+ case QPainter::CompositionMode_DestinationIn:
+ return QLatin1String("destination-in");
+ case QPainter::CompositionMode_SourceOut:
+ return QLatin1String("source-out");
+ case QPainter::CompositionMode_DestinationOut:
+ return QLatin1String("destination-out");
+ case QPainter::CompositionMode_SourceAtop:
+ return QLatin1String("source-atop");
+ case QPainter::CompositionMode_DestinationAtop:
+ return QLatin1String("destination-atop");
+ case QPainter::CompositionMode_Xor:
+ return QLatin1String("xor");
+ case QPainter::CompositionMode_Plus:
+ return QLatin1String("plus");
+ case QPainter::CompositionMode_Multiply:
+ return QLatin1String("qt-multiply");
+ case QPainter::CompositionMode_Screen:
+ return QLatin1String("qt-screen");
+ case QPainter::CompositionMode_Overlay:
+ return QLatin1String("qt-overlay");
+ case QPainter::CompositionMode_Darken:
+ return QLatin1String("qt-darken");
+ case QPainter::CompositionMode_Lighten:
+ return QLatin1String("lighter");
+ case QPainter::CompositionMode_ColorDodge:
+ return QLatin1String("qt-color-dodge");
+ case QPainter::CompositionMode_ColorBurn:
+ return QLatin1String("qt-color-burn");
+ case QPainter::CompositionMode_HardLight:
+ return QLatin1String("qt-hard-light");
+ case QPainter::CompositionMode_SoftLight:
+ return QLatin1String("qt-soft-light");
+ case QPainter::CompositionMode_Difference:
+ return QLatin1String("qt-difference");
+ case QPainter::CompositionMode_Exclusion:
+ return QLatin1String("qt-exclusion");
+ default:
+ break;
+ }
+ return QString();
+}
+
+
+static v8::Local<v8::Object> qt_create_image_data(qreal w, qreal h, QV8Engine* engine, const QImage& image)
+{
+ QQuickContext2DEngineData *ed = engineData(engine);
+ v8::Local<v8::Object> imageData = ed->constructorImageData->NewInstance();
+ QV8Context2DPixelArrayResource *r = new QV8Context2DPixelArrayResource(engine);
+ if (image.isNull()) {
+ r->image = QImage(w, h, QImage::Format_ARGB32);
+ r->image.fill(0x00000000);
+ } else {
+ Q_ASSERT(image.width() == w && image.height() == h);
+ r->image = image.format() == QImage::Format_ARGB32 ? image : image.convertToFormat(QImage::Format_ARGB32);
+ }
+ v8::Local<v8::Object> pixelData = ed->constructorPixelArray->NewInstance();
+ pixelData->SetExternalResource(r);
+
+ imageData->SetInternalField(0, pixelData);
+ return imageData;
+}
+
+//static script functions
+
+/*!
+ \qmlproperty QtQuick2::Canvas QtQuick2::Context2D::canvas
+ Holds the canvas item that the context paints on.
+
+ This property is read only.
+*/
+static v8::Handle<v8::Value> ctx2d_canvas(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+
+
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+
+ return engine->newQObject(r->context->canvas());
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::restore()
+ Pops the top state on the stack, restoring the context to that state.
+
+ \sa QtQuick2::Context2D::save()
+*/
+static v8::Handle<v8::Value> ctx2d_restore(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ r->context->popState();
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::reset()
+ Resets the context state and properties to the default values.
+*/
+static v8::Handle<v8::Value> ctx2d_reset(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ r->context->reset();
+ r->context->m_path = QPainterPath();
+ r->context->m_path.setFillRule(Qt::WindingFill);
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::save()
+ Pushes the current state onto the state stack.
+
+ Before changing any state attributes, you should save the current state
+ for future reference. The context maintains a stack of drawing states.
+ Each state consists of the current transformation matrix, clipping region,
+ and values of the following attributes:
+ \list
+ \o\a QtQuick2::Context2D::strokeStyle
+ \o\a QtQuick2::Context2D::fillStyle
+ \o\a QtQuick2::Context2D::fillRule
+ \o\a QtQuick2::Context2D::globalAlpha
+ \o\a QtQuick2::Context2D::lineWidth
+ \o\a QtQuick2::Context2D::lineCap
+ \o\a QtQuick2::Context2D::lineJoin
+ \o\a QtQuick2::Context2D::miterLimit
+ \o\a QtQuick2::Context2D::shadowOffsetX
+ \o\a QtQuick2::Context2D::shadowOffsetY
+ \o\a QtQuick2::Context2D::shadowBlur
+ \o\a QtQuick2::Context2D::shadowColor
+ \o\a QtQuick2::Context2D::globalCompositeOperation
+ \o\a QtQuick2::Context2D::font
+ \o\a QtQuick2::Context2D::textAlign
+ \o\a QtQuick2::Context2D::textBaseline
+ \endlist
+
+ The current path is NOT part of the drawing state. The path can be reset by
+ invoking the \a QtQuick2::Context2D::beginPath() method.
+*/
+static v8::Handle<v8::Value> ctx2d_save(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ r->context->pushState();
+
+ return args.This();
+}
+
+// transformations
+/*!
+ \qmlmethod object QtQuick2::Context2D::rotate(real angle)
+ Rotate the canvas around the current origin by \c angle in radians and clockwise direction.
+ \code
+ ctx.rotate(Math.PI/2);
+ \endcode
+ \image qml-item-canvas-rotate.png
+
+ The rotation transformation matrix is as follows:
+
+ \image qml-item-canvas-math-rotate.png
+
+ where the \c angle of rotation is in radians.
+
+*/
+static v8::Handle<v8::Value> ctx2d_rotate(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ if (args.Length() == 1) {
+ qreal angle = args[0]->NumberValue();
+ if (!qIsFinite(angle))
+ return args.This();
+
+ r->context->state.matrix.rotate(DEGREES(angle));
+ r->context->buffer()->updateMatrix(r->context->state.matrix);
+ }
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::scale(real x, real y)
+ Increases or decreases the size of each unit in the canvas grid by multiplying the scale factors
+ to the current tranform matrix.
+ Where \c x is the scale factor in the horizontal direction and \c y is the scale factor in the
+ vertical direction.
+ The following code doubles the horizontal size of an object drawn on the canvas and half its
+ vertical size:
+ \code
+ ctx.scale(2.0, 0.5);
+ \endcode
+ \image qml-item-canvas-scale.png
+
+*/
+static v8::Handle<v8::Value> ctx2d_scale(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ if (args.Length() == 2) {
+ qreal x, y;
+ x = args[0]->NumberValue();
+ y = args[1]->NumberValue();
+ if (!qIsFinite(x) || !qIsFinite(y))
+ return args.This();
+
+ r->context->state.matrix.scale(x, y);
+ r->context->buffer()->updateMatrix(r->context->state.matrix);
+ }
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::setTransform(real a, real b, real c, real d, real e, real f)
+ Changes the transformation matrix to the matrix given by the arguments as described below.
+
+ Modifying the transformation matrix directly enables you to perform scaling,
+ rotating, and translating transformations in a single step.
+
+ Each point on the canvas is multiplied by the matrix before anything is
+ drawn. The \l{HTML5 Canvas API} defines the transformation matrix as:
+
+ \image qml-item-canvas-math.png
+ where:
+ \list
+ \o \c{a} is the scale factor in the horizontal (x) direction
+ \image qml-item-canvas-scalex.png
+ \o \c{c} is the skew factor in the x direction
+ \image qml-item-canvas-canvas-skewx.png
+ \o \c{e} is the translation in the x direction
+ \image qml-item-canvas-canvas-translate.png
+ \o \c{b} is the skew factor in the y (vertical) direction
+ \image qml-item-canvas-canvas-skewy.png
+ \o \c{d} is the scale factor in the y direction
+ \image qml-item-canvas-canvas-scaley.png
+ \o \c{f} is the translation in the y direction
+ \image qml-item-canvas-canvas-translatey.png
+ \o the last row remains constant
+ \endlist
+ The scale factors and skew factors are multiples; \c{e} and \c{f} are
+ coordinate space units, just like the units in the \a QtQuick2::Context2D::translate(x,y)
+ method.
+
+ \sa QtQuick2::Context2D::transform()
+*/
+static v8::Handle<v8::Value> ctx2d_setTransform(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ if (args.Length() == 6) {
+ qreal a = args[0]->NumberValue();
+ qreal b = args[1]->NumberValue();
+ qreal c = args[2]->NumberValue();
+ qreal d = args[3]->NumberValue();
+ qreal e = args[4]->NumberValue();
+ qreal f = args[5]->NumberValue();
+
+ if (!qIsFinite(a)
+ || !qIsFinite(b)
+ || !qIsFinite(c)
+ || !qIsFinite(d)
+ || !qIsFinite(e)
+ || !qIsFinite(f))
+ return args.This();
+
+ r->context->state.matrix = QTransform(a, b, c, d, e, f);
+ r->context->buffer()->updateMatrix(r->context->state.matrix);
+ }
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::transform(real a, real b, real c, real d, real e, real f)
+ This method is very similar to \a QtQuick2::Context2D::setTransform(), but instead of replacing the old
+ tranform matrix, this method applies the given tranform matrix to the current matrix by mulitplying to it.
+
+ The \a setTransform(a, b, c, d, e, f) method actually resets the current transform to the identity matrix,
+ and then invokes the transform(a, b, c, d, e, f) method with the same arguments.
+
+ \sa QtQuick2::Context2D::setTransform()
+*/
+static v8::Handle<v8::Value> ctx2d_transform(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ if (args.Length() == 6) {
+ qreal a = args[0]->NumberValue();
+ qreal b = args[1]->NumberValue();
+ qreal c = args[2]->NumberValue();
+ qreal d = args[3]->NumberValue();
+ qreal e = args[4]->NumberValue();
+ qreal f = args[5]->NumberValue();
+
+ if (!qIsFinite(a)
+ || !qIsFinite(b)
+ || !qIsFinite(c)
+ || !qIsFinite(d)
+ || !qIsFinite(e)
+ || !qIsFinite(f))
+ return args.This();
+
+ r->context->state.matrix *= QTransform(a, b, c, d, e, f);
+ r->context->buffer()->updateMatrix(r->context->state.matrix);
+ }
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::translate(real x, real y)
+ Translates the origin of the canvas to point (\c x, \c y).
+
+ \c x is the horizontal distance that the origin is translated, in coordinate space units,
+ \c y is the vertical distance that the origin is translated, in coordinate space units.
+ Translating the origin enables you to draw patterns of different objects on the canvas
+ without having to measure the coordinates manually for each shape.
+*/
+static v8::Handle<v8::Value> ctx2d_translate(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ if (args.Length() == 2) {
+ qreal x = args[0]->NumberValue();
+ qreal y = args[1]->NumberValue();
+
+ if (!qIsFinite(x) || !qIsFinite(y))
+ return args.This();
+
+ r->context->state.matrix.translate(x, y);
+ r->context->buffer()->updateMatrix(r->context->state.matrix);
+ }
+
+ return args.This();
+}
+
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::resetTransform()
+ Reset the transformation matrix to default value.
+
+ \sa QtQuick2::Context2D::transform(), QtQuick2::Context2D::setTransform(), QtQuick2::Context2D::reset()
+*/
+static v8::Handle<v8::Value> ctx2d_resetTransform(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ r->context->state.matrix = QTransform();
+ r->context->buffer()->updateMatrix(r->context->state.matrix);
+
+ return args.This();
+}
+
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::shear(real sh, real sv )
+ Shear the transformation matrix with \a sh in horizontal direction and \a sv in vertical direction.
+*/
+static v8::Handle<v8::Value> ctx2d_shear(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ if (args.Length() == 2) {
+ qreal sh = args[0]->NumberValue();
+ qreal sv = args[1]->NumberValue();
+
+ if (!qIsFinite(sh) || !qIsFinite(sv))
+ return args.This();
+
+ r->context->state.matrix.shear(sh, sv);
+ r->context->buffer()->updateMatrix(r->context->state.matrix);
+ }
+ return args.This();
+}
+// compositing
+
+/*!
+ \qmlproperty real QtQuick2::Context2D::globalAlpha
+ Holds the the current alpha value applied to rendering operations.
+ The value must be in the range from 0.0 (fully transparent) to 1.0 (fully opque).
+ The default value is 1.0.
+*/
+static v8::Handle<v8::Value> ctx2d_globalAlpha(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+
+ return v8::Number::New(r->context->state.globalAlpha);
+}
+
+static void ctx2d_globalAlpha_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT_SETTER(r)
+
+ qreal globalAlpha = value->NumberValue();
+
+ if (!qIsFinite(globalAlpha))
+ return;
+
+ if (globalAlpha >= 0.0 && globalAlpha <= 1.0 && r->context->state.globalAlpha != globalAlpha) {
+ r->context->state.globalAlpha = globalAlpha;
+ r->context->buffer()->setGlobalAlpha(r->context->state.globalAlpha);
+ }
+}
+
+/*!
+ \qmlproperty string QtQuick2::Context2D::globalCompositeOperation
+ Holds the the current the current composition operation, from the list below:
+ \list
+ \o source-atop - A atop B. Display the source image wherever both images are opaque.
+ Display the destination image wherever the destination image is opaque but the source image is transparent.
+ Display transparency elsewhere.
+ \o source-in - A in B. Display the source image wherever both the source image and destination image are opaque.
+ Display transparency elsewhere.
+ \o source-out - A out B. Display the source image wherever the source image is opaque and the destination image is transparent.
+ Display transparency elsewhere.
+ \o source-over - (default) A over B. Display the source image wherever the source image is opaque.
+ Display the destination image elsewhere.
+ \o destination-atop - B atop A. Same as source-atop but using the destination image instead of the source image and vice versa.
+ \o destination-in - B in A. Same as source-in but using the destination image instead of the source image and vice versa.
+ \o destination-out - B out A. Same as source-out but using the destination image instead of the source image and vice versa.
+ \o destination-over - B over A. Same as source-over but using the destination image instead of the source image and vice versa.
+ \o lighter - A plus B. Display the sum of the source image and destination image, with color values approaching 255 (100%) as a limit.
+ \o copy - A (B is ignored). Display the source image instead of the destination image.
+ \o xor - A xor B. Exclusive OR of the source image and destination image.
+ \endlist
+
+ Additionally, this property also accepts the compositon modes listed in \a {QPainter::CompositionMode}. According to the W3C standard, these
+ extension composition modes are provided as "vendorName-operationName" syntax, for example: \c {QPainter::CompositionMode_Exclusion} is porvided as
+ "qt-exclusion".
+*/
+static v8::Handle<v8::Value> ctx2d_globalCompositeOperation(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+
+
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+
+ return engine->toString(qt_composite_mode_to_string(r->context->state.globalCompositeOperation));
+}
+
+static void ctx2d_globalCompositeOperation_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT_SETTER(r)
+
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+
+
+ QString mode = engine->toString(value);
+ QPainter::CompositionMode cm = qt_composite_mode_from_string(mode);
+ if (cm == QPainter::CompositionMode_SourceOver && mode != QStringLiteral("source-over"))
+ return;
+
+ if (cm != r->context->state.globalCompositeOperation) {
+ r->context->state.globalCompositeOperation = cm;
+ r->context->buffer()->setGlobalCompositeOperation(cm);
+ }
+}
+
+// colors and styles
+/*!
+ \qmlproperty variant QtQuick2::Context2D::fillStyle
+ Holds the current style used for filling shapes.
+ The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object. Invalid values are ignored.
+ This property accepts several color syntaxes:
+ \list
+ \o 'rgb(red, green, blue)' - for example: 'rgb(255, 100, 55)' or 'rgb(100%, 70%, 30%)'
+ \o 'rgba(red, green, blue, alpha)' - for example: 'rgb(255, 100, 55, 1.0)' or 'rgb(100%, 70%, 30%, 0.5)'
+ \o 'hsl(hue, saturation, lightness)'
+ \o 'hsla(hue, saturation, lightness, alpha)'
+ \o '#RRGGBB' - for example: '#00FFCC'
+ \o Qt.rgba(red, green, blue, alpha) - for example: Qt.rgba(0.3, 0.7, 1, 1.0)
+ \endlist
+ If the \a fillStyle or \a strokeStyle is assigned many times in a loop, the last Qt.rgba() syntax should be chosen, as it has the
+ best performance, because it's already a valid QColor value, does not need to be parsed everytime.
+
+ The default value is '#000000'.
+ \sa QtQuick2::Context2D::createLinearGradient
+ \sa QtQuick2::Context2D::createRadialGradient
+ \sa QtQuick2::Context2D::createPattern
+ \sa QtQuick2::Context2D::strokeStyle
+ */
+static v8::Handle<v8::Value> ctx2d_fillStyle(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+
+ QColor color = r->context->state.fillStyle.color();
+ if (color.isValid()) {
+ if (color.alpha() == 255)
+ return engine->toString(color.name());
+ QString alphaString = QString::number(color.alphaF(), 'f');
+ while (alphaString.endsWith(QLatin1Char('0')))
+ alphaString.chop(1);
+ if (alphaString.endsWith(QLatin1Char('.')))
+ alphaString += QLatin1Char('0');
+ return engine->toString(QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString));
+ }
+ return r->context->m_fillStyle;
+}
+
+static void ctx2d_fillStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT_SETTER(r)
+
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+
+ if (value->IsObject()) {
+ QColor color = engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
+ if (color.isValid()) {
+ r->context->state.fillStyle = color;
+ r->context->buffer()->setFillStyle(color);
+ r->context->m_fillStyle = value;
+ } else {
+ QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(value->ToObject());
+ if (style && style->brush != r->context->state.fillStyle) {
+ r->context->state.fillStyle = style->brush;
+ r->context->buffer()->setFillStyle(style->brush, style->patternRepeatX, style->patternRepeatY);
+ r->context->m_fillStyle = value;
+ r->context->state.fillPatternRepeatX = style->patternRepeatX;
+ r->context->state.fillPatternRepeatY = style->patternRepeatY;
+ }
+ }
+ } else if (value->IsString()) {
+ QColor color = qt_color_from_string(value);
+ if (color.isValid() && r->context->state.fillStyle != QBrush(color)) {
+ r->context->state.fillStyle = QBrush(color);
+ r->context->buffer()->setFillStyle(r->context->state.fillStyle);
+ r->context->m_fillStyle = value;
+ }
+ }
+}
+/*!
+ \qmlproperty enumeration QtQuick2::Context2D::fillRule
+ Holds the current fill rule used for filling shapes. The following fill rules supported:
+ \list
+ \o Qt.OddEvenFill
+ \o Qt.WindingFill
+ \endlist
+ Note: Unlike the \a QPainterPath, the Canvas API uses the winding fill as the default fill rule.
+ The fillRule property is part of the context rendering state.
+
+ \sa QtQuick2::Context2D::fillStyle
+ */
+static v8::Handle<v8::Value> ctx2d_fillRule(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+
+ return engine->fromVariant(r->context->state.fillRule);
+}
+
+static void ctx2d_fillRule_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT_SETTER(r)
+
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+
+ if ((value->IsString() && engine->toString(value) == QStringLiteral("WindingFill"))
+ ||(value->IsNumber() && value->NumberValue() == Qt::WindingFill)) {
+ r->context->state.fillRule = Qt::WindingFill;
+ } else if ((value->IsString() && engine->toString(value) == QStringLiteral("OddEvenFill"))
+ ||(value->IsNumber() && value->NumberValue() == Qt::OddEvenFill)) {
+ r->context->state.fillRule = Qt::OddEvenFill;
+ } else {
+ //error
+ }
+ r->context->m_path.setFillRule(r->context->state.fillRule);
+}
+/*!
+ \qmlproperty variant QtQuick2::Context2D::strokeStyle
+ Holds the current color or style to use for the lines around shapes,
+ The style can be either a string containing a CSS color, a CanvasGradient or CanvasPattern object.
+ Invalid values are ignored.
+
+ The default value is '#000000'.
+
+ \sa QtQuick2::Context2D::createLinearGradient
+ \sa QtQuick2::Context2D::createRadialGradient
+ \sa QtQuick2::Context2D::createPattern
+ \sa QtQuick2::Context2D::fillStyle
+ */
+v8::Handle<v8::Value> ctx2d_strokeStyle(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+
+ QColor color = r->context->state.strokeStyle.color();
+ if (color.isValid()) {
+ if (color.alpha() == 255)
+ return engine->toString(color.name());
+ QString alphaString = QString::number(color.alphaF(), 'f');
+ while (alphaString.endsWith(QLatin1Char('0')))
+ alphaString.chop(1);
+ if (alphaString.endsWith(QLatin1Char('.')))
+ alphaString += QLatin1Char('0');
+ return engine->toString(QString::fromLatin1("rgba(%1, %2, %3, %4)").arg(color.red()).arg(color.green()).arg(color.blue()).arg(alphaString));
+ }
+ return r->context->m_strokeStyle;
+}
+
+static void ctx2d_strokeStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT_SETTER(r)
+
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+
+ if (value->IsObject()) {
+ QColor color = engine->toVariant(value, qMetaTypeId<QColor>()).value<QColor>();
+ if (color.isValid()) {
+ r->context->state.fillStyle = color;
+ r->context->buffer()->setStrokeStyle(color);
+ r->context->m_strokeStyle = value;
+ } else {
+ QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(value->ToObject());
+ if (style && style->brush != r->context->state.strokeStyle) {
+ r->context->state.strokeStyle = style->brush;
+ r->context->buffer()->setStrokeStyle(style->brush, style->patternRepeatX, style->patternRepeatY);
+ r->context->m_strokeStyle = value;
+ r->context->state.strokePatternRepeatX = style->patternRepeatX;
+ r->context->state.strokePatternRepeatY = style->patternRepeatY;
+
+ }
+ }
+ } else if (value->IsString()) {
+ QColor color = qt_color_from_string(value);
+ if (color.isValid() && r->context->state.strokeStyle != QBrush(color)) {
+ r->context->state.strokeStyle = QBrush(color);
+ r->context->buffer()->setStrokeStyle(r->context->state.strokeStyle);
+ r->context->m_strokeStyle = value;
+ }
+ }
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::createLinearGradient(real x0, real y0, real x1, real y1)
+ Returns a CanvasGradient object that represents a linear gradient that transitions the color along a line between
+ the start point (\a x0, \a y0) and the end point (\a x1, \a y1).
+
+ A gradient is a smooth transition between colors. There are two types of gradients: linear and radial.
+ Gradients must have two or more color stops, representing color shifts positioned from 0 to 1 between
+ to the gradient's starting and end points or circles.
+
+ \sa QtQuick2::Context2D::CanvasGradient::addColorStop
+ \sa QtQuick2::Context2D::createRadialGradient
+ \sa QtQuick2::Context2D::ctx2d_createConicalGradient
+ \sa QtQuick2::Context2D::createPattern
+ \sa QtQuick2::Context2D::fillStyle
+ \sa QtQuick2::Context2D::strokeStyle
+ */
+
+static v8::Handle<v8::Value> ctx2d_createLinearGradient(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ QV8Engine *engine = V8ENGINE();
+
+ if (args.Length() == 4) {
+ QQuickContext2DEngineData *ed = engineData(engine);
+ v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
+ QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
+ qreal x0 = args[0]->NumberValue();
+ qreal y0 = args[1]->NumberValue();
+ qreal x1 = args[2]->NumberValue();
+ qreal y1 = args[3]->NumberValue();
+
+ if (!qIsFinite(x0)
+ || !qIsFinite(y0)
+ || !qIsFinite(x1)
+ || !qIsFinite(y1))
+ V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createLinearGradient(): Incorrect arguments")
+
+ r->brush = QLinearGradient(x0, y0, x1, y1);
+ gradient->SetExternalResource(r);
+ return gradient;
+ }
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::createRadialGradient(real x0, real y0, real r0, real x1, real y1, real r1)
+ Returns a CanvasGradient object that represents a radial gradient that paints along the cone given by the start circle with
+ origin (x0, y0) and radius r0, and the end circle with origin (x1, y1) and radius r1.
+
+ \sa QtQuick2::Context2D::CanvasGradient::addColorStop
+ \sa QtQuick2::Context2D::createLinearGradient
+ \sa QtQuick2::Context2D::ctx2d_createConicalGradient
+ \sa QtQuick2::Context2D::createPattern
+ \sa QtQuick2::Context2D::fillStyle
+ \sa QtQuick2::Context2D::strokeStyle
+ */
+
+static v8::Handle<v8::Value> ctx2d_createRadialGradient(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ QV8Engine *engine = V8ENGINE();
+
+ if (args.Length() == 6) {
+ QQuickContext2DEngineData *ed = engineData(engine);
+ v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
+ QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
+
+ qreal x0 = args[0]->NumberValue();
+ qreal y0 = args[1]->NumberValue();
+ qreal r0 = args[2]->NumberValue();
+ qreal x1 = args[3]->NumberValue();
+ qreal y1 = args[4]->NumberValue();
+ qreal r1 = args[5]->NumberValue();
+
+ if (!qIsFinite(x0)
+ || !qIsFinite(y0)
+ || !qIsFinite(x1)
+ || !qIsFinite(r0)
+ || !qIsFinite(r1)
+ || !qIsFinite(y1))
+ V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createRadialGradient(): Incorrect arguments")
+
+ if (r0 < 0 || r1 < 0)
+ V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createRadialGradient(): Incorrect arguments")
+
+
+ r->brush = QRadialGradient(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
+ gradient->SetExternalResource(r);
+ return gradient;
+ }
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::createConicalGradient(real x, real y, real angle)
+ Returns a CanvasGradient object that represents a conical gradient that interpolate colors counter-clockwise around a center point (\c x, \c y)
+ with start angle \c angle in units of radians.
+
+ \sa QtQuick2::Context2D::CanvasGradient::addColorStop
+ \sa QtQuick2::Context2D::createLinearGradient
+ \sa QtQuick2::Context2D::ctx2d_createRadialGradient
+ \sa QtQuick2::Context2D::createPattern
+ \sa QtQuick2::Context2D::fillStyle
+ \sa QtQuick2::Context2D::strokeStyle
+ */
+
+static v8::Handle<v8::Value> ctx2d_createConicalGradient(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ QV8Engine *engine = V8ENGINE();
+
+ if (args.Length() == 6) {
+ QQuickContext2DEngineData *ed = engineData(engine);
+ v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
+ QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
+
+ qreal x = args[0]->NumberValue();
+ qreal y = args[1]->NumberValue();
+ qreal angle = DEGREES(args[2]->NumberValue());
+ if (!qIsFinite(x) || !qIsFinite(y))
+ V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createConicalGradient(): Incorrect arguments");
+
+ if (!qIsFinite(angle))
+ V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createConicalGradient(): Incorrect arguments");
+
+ r->brush = QConicalGradient(x, y, angle);
+ gradient->SetExternalResource(r);
+ return gradient;
+ }
+
+ return args.This();
+}
+/*!
+ \qmlmethod variant createPattern(Color color, enumeration patternMode)
+ This is a overload function.
+ Returns a CanvasPattern object that uses the given \c color and \c patternMode.
+ The valid pattern modes are:
+ \list
+ \o Qt.SolidPattern
+ \o Qt.Dense1Pattern
+ \o Qt.Dense2Pattern
+ \o Qt.Dense3Pattern
+ \o Qt.Dense4Pattern
+ \o Qt.Dense5Pattern
+ \o Qt.Dense6Pattern
+ \o Qt.Dense7Pattern
+ \o Qt.HorPattern
+ \o Qt.VerPattern
+ \o Qt.CrossPattern
+ \o Qt.BDiagPattern
+ \o Qt.FDiagPattern
+ \o Qt.DiagCrossPattern
+\endlist
+ \sa Qt::BrushStyle
+ */
+/*!
+ \qmlmethod variant createPattern(Image image, string repetition)
+ Returns a CanvasPattern object that uses the given image and repeats in the direction(s) given by the repetition argument.
+
+ The \a image parameter must be a valid Image item, a valid \a QtQuick2::CanvasImageData object or loaded image url, if there is no image data, throws an INVALID_STATE_ERR exception.
+
+ The allowed values for \a repetition are:
+
+ \list
+ \o "repeat" - both directions
+ \o "repeat-x - horizontal only
+ \o "repeat-y" - vertical only
+ \o "no-repeat" - neither
+ \endlist
+
+ If the repetition argument is empty or null, the value "repeat" is used.
+
+ \sa QtQuick2::Context2D::strokeStyle
+ \sa QtQuick2::Context2D::fillStyle
+ */
+static v8::Handle<v8::Value> ctx2d_createPattern(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ QV8Engine *engine = V8ENGINE();
+
+ if (args.Length() == 2) {
+ QQuickContext2DEngineData *ed = engineData(engine);
+ QV8Context2DStyleResource *styleResouce = new QV8Context2DStyleResource(engine);
+
+ QColor color = engine->toVariant(args[0], qMetaTypeId<QColor>()).value<QColor>();
+ if (color.isValid()) {
+ int patternMode = args[1]->IntegerValue();
+ Qt::BrushStyle style = Qt::SolidPattern;
+ if (patternMode >= 0 && patternMode < Qt::LinearGradientPattern) {
+ style = static_cast<Qt::BrushStyle>(patternMode);
+ }
+ styleResouce->brush = QBrush(color, style);
+ } else {
+ QImage patternTexture;
+
+ if (args[0]->IsObject()) {
+ QV8Context2DPixelArrayResource *pixelData = v8_resource_cast<QV8Context2DPixelArrayResource>(args[0]->ToObject()->Get(v8::String::New("data"))->ToObject());
+ if (pixelData) {
+ patternTexture = pixelData->image;
+ }
+ } else {
+ patternTexture = r->context->createImage(QUrl(engine->toString(args[0]->ToString())));
+ }
+
+ if (!patternTexture.isNull()) {
+ styleResouce->brush.setTextureImage(patternTexture);
+
+ QString repetition = engine->toString(args[1]);
+ if (repetition == QStringLiteral("repeat") || repetition.isEmpty()) {
+ styleResouce->patternRepeatX = true;
+ styleResouce->patternRepeatY = true;
+ } else if (repetition == QStringLiteral("repeat-x")) {
+ styleResouce->patternRepeatX = true;
+ } else if (repetition == QStringLiteral("repeat-y")) {
+ styleResouce->patternRepeatY = true;
+ } else if (repetition == QStringLiteral("no-repeat")) {
+ styleResouce->patternRepeatY = false;
+ styleResouce->patternRepeatY = false;
+ } else {
+ //TODO: exception: SYNTAX_ERR
+ }
+
+ }
+ }
+
+ v8::Local<v8::Object> pattern = ed->constructorPattern->NewInstance();
+ pattern->SetExternalResource(styleResouce);
+ return pattern;
+
+ }
+ return v8::Undefined();
+}
+
+// line styles
+/*!
+ \qmlproperty string QtQuick2::Context2D::lineCap
+ Holds the the current line cap style.
+ The possible line cap styles are:
+ \list
+ \o butt - the end of each line has a flat edge perpendicular to the direction of the line, this is the default line cap value.
+ \o round - a semi-circle with the diameter equal to the width of the line must then be added on to the end of the line.
+ \o square - a rectangle with the length of the line width and the width of half the line width, placed flat against the edge perpendicular to the direction of the line.
+ \endlist
+ Other values are ignored.
+*/
+v8::Handle<v8::Value> ctx2d_lineCap(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+
+
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+ switch (r->context->state.lineCap) {
+ case Qt::RoundCap:
+ return engine->toString(QLatin1String("round"));
+ case Qt::FlatCap:
+ return engine->toString(QLatin1String("butt"));
+ case Qt::SquareCap:
+ return engine->toString(QLatin1String("square"));
+ default:
+ break;
+ }
+ return engine->toString(QLatin1String("butt"));;
+}
+
+static void ctx2d_lineCap_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT_SETTER(r)
+
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+
+ QString lineCap = engine->toString(value);
+ Qt::PenCapStyle cap;
+ if (lineCap == QLatin1String("round"))
+ cap = Qt::RoundCap;
+ else if (lineCap == QLatin1String("butt"))
+ cap = Qt::FlatCap;
+ else if (lineCap == QLatin1String("square"))
+ cap = Qt::SquareCap;
+ else
+ return;
+
+ if (cap != r->context->state.lineCap) {
+ r->context->state.lineCap = cap;
+ r->context->buffer()->setLineCap(cap);
+ }
+}
+
+/*!
+ \qmlproperty string QtQuick2::Context2D::lineJoin
+ Holds the the current line join style. A join exists at any point in a subpath
+ shared by two consecutive lines. When a subpath is closed, then a join also exists
+ at its first point (equivalent to its last point) connecting the first and last lines in the subpath.
+
+ The possible line join styles are:
+ \list
+ \o bevel - this is all that is rendered at joins.
+ \o round - a filled arc connecting the two aforementioned corners of the join, abutting (and not overlapping) the aforementioned triangle, with the diameter equal to the line width and the origin at the point of the join, must be rendered at joins.
+ \o miter - a second filled triangle must (if it can given the miter length) be rendered at the join, this is the default line join style.
+ \endlist
+ Other values are ignored.
+*/
+v8::Handle<v8::Value> ctx2d_lineJoin(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+
+
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+ switch (r->context->state.lineJoin) {
+ case Qt::RoundJoin:
+ return engine->toString(QLatin1String("round"));
+ case Qt::BevelJoin:
+ return engine->toString(QLatin1String("bevel"));
+ case Qt::MiterJoin:
+ return engine->toString(QLatin1String("miter"));
+ default:
+ break;
+ }
+ return engine->toString(QLatin1String("miter"));
+}
+
+static void ctx2d_lineJoin_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT_SETTER(r)
+
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+
+ QString lineJoin = engine->toString(value);
+ Qt::PenJoinStyle join;
+ if (lineJoin == QLatin1String("round"))
+ join = Qt::RoundJoin;
+ else if (lineJoin == QLatin1String("bevel"))
+ join = Qt::BevelJoin;
+ else if (lineJoin == QLatin1String("miter"))
+ join = Qt::MiterJoin;
+ else
+ return;
+
+ if (join != r->context->state.lineJoin) {
+ r->context->state.lineJoin = join;
+ r->context->buffer()->setLineJoin(join);
+ }
+}
+
+/*!
+ \qmlproperty real QtQuick2::Context2D::lineWidth
+ Holds the the current line width. Values that are not finite values greater than zero are ignored.
+ */
+v8::Handle<v8::Value> ctx2d_lineWidth(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+
+
+ return v8::Number::New(r->context->state.lineWidth);
+}
+
+static void ctx2d_lineWidth_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT_SETTER(r)
+
+ qreal w = value->NumberValue();
+
+ if (w > 0 && qIsFinite(w) && w != r->context->state.lineWidth) {
+ r->context->state.lineWidth = w;
+ r->context->buffer()->setLineWidth(w);
+ }
+}
+
+/*!
+ \qmlproperty real QtQuick2::Context2D::miterLimit
+ Holds the current miter limit ratio.
+ The default miter limit value is 10.0.
+ */
+v8::Handle<v8::Value> ctx2d_miterLimit(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+
+
+ return v8::Number::New(r->context->state.miterLimit);
+}
+
+static void ctx2d_miterLimit_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT_SETTER(r)
+
+ qreal ml = value->NumberValue();
+
+ if (ml > 0 && qIsFinite(ml) && ml != r->context->state.miterLimit) {
+ r->context->state.miterLimit = ml;
+ r->context->buffer()->setMiterLimit(ml);
+ }
+}
+
+// shadows
+/*!
+ \qmlproperty real QtQuick2::Context2D::shadowBlur
+ Holds the current level of blur applied to shadows
+ */
+v8::Handle<v8::Value> ctx2d_shadowBlur(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+
+
+ return v8::Number::New(r->context->state.shadowBlur);
+}
+
+static void ctx2d_shadowBlur_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT_SETTER(r)
+ qreal blur = value->NumberValue();
+
+ if (blur > 0 && qIsFinite(blur) && blur != r->context->state.shadowBlur) {
+ r->context->state.shadowBlur = blur;
+ r->context->buffer()->setShadowBlur(blur);
+ }
+}
+
+/*!
+ \qmlproperty string QtQuick2::Context2D::shadowColor
+ Holds the current shadow color.
+ */
+v8::Handle<v8::Value> ctx2d_shadowColor(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+
+
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+
+ return engine->toString(r->context->state.shadowColor.name());
+}
+
+static void ctx2d_shadowColor_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT_SETTER(r)
+
+ QColor color = qt_color_from_string(value);
+
+ if (color.isValid() && color != r->context->state.shadowColor) {
+ r->context->state.shadowColor = color;
+ r->context->buffer()->setShadowColor(color);
+ }
+}
+
+
+/*!
+ \qmlproperty qreal QtQuick2::Context2D::shadowOffsetX
+ Holds the current shadow offset in the positive horizontal distance.
+
+ \sa QtQuick2::Context2D::shadowOffsetY
+ */
+v8::Handle<v8::Value> ctx2d_shadowOffsetX(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+
+
+ return v8::Number::New(r->context->state.shadowOffsetX);
+}
+
+static void ctx2d_shadowOffsetX_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT_SETTER(r)
+
+ qreal offsetX = value->NumberValue();
+ if (qIsFinite(offsetX) && offsetX != r->context->state.shadowOffsetX) {
+ r->context->state.shadowOffsetX = offsetX;
+ r->context->buffer()->setShadowOffsetX(offsetX);
+ }
+}
+/*!
+ \qmlproperty qreal QtQuick2::Context2D::shadowOffsetY
+ Holds the current shadow offset in the positive vertical distance.
+
+ \sa QtQuick2::Context2D::shadowOffsetX
+ */
+v8::Handle<v8::Value> ctx2d_shadowOffsetY(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+
+
+ return v8::Number::New(r->context->state.shadowOffsetY);
+}
+
+static void ctx2d_shadowOffsetY_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT_SETTER(r)
+
+ qreal offsetY = value->NumberValue();
+ if (qIsFinite(offsetY) && offsetY != r->context->state.shadowOffsetY) {
+ r->context->state.shadowOffsetY = offsetY;
+ r->context->buffer()->setShadowOffsetY(offsetY);
+ }
+}
+
+v8::Handle<v8::Value> ctx2d_path(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+ return r->context->m_v8path;
+}
+
+static void ctx2d_path_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT_SETTER(r)
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+
+ r->context->beginPath();
+ if (value->IsObject()) {
+ QDeclarativePath* path = qobject_cast<QDeclarativePath*>(engine->toQObject(value));
+ if (path)
+ r->context->m_path = path->path();
+ } else {
+ QString path = engine->toString(value->ToString());
+ QDeclarativeSvgParser::parsePathDataFast(path, r->context->m_path);
+ }
+ r->context->m_v8path = value;
+}
+
+//rects
+/*!
+ \qmlmethod object QtQuick2::Context2D::clearRect(real x, real y, real w, real h)
+ Clears all pixels on the canvas in the given rectangle to transparent black.
+ */
+static v8::Handle<v8::Value> ctx2d_clearRect(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ if (args.Length() == 4) {
+ qreal x = args[0]->NumberValue();
+ qreal y = args[1]->NumberValue();
+ qreal w = args[2]->NumberValue();
+ qreal h = args[3]->NumberValue();
+
+ if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
+ return args.This();
+
+ r->context->buffer()->clearRect(x, y, w, h);
+ }
+
+ return args.This();
+}
+/*!
+ \qmlmethod object QtQuick2::Context2D::fillRect(real x, real y, real w, real h)
+ Paint the specified rectangular area using the fillStyle.
+
+ \sa QtQuick2::Context2D::fillStyle
+ */
+static v8::Handle<v8::Value> ctx2d_fillRect(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ if (args.Length() == 4) {
+ qreal x = args[0]->NumberValue();
+ qreal y = args[1]->NumberValue();
+ qreal w = args[2]->NumberValue();
+ qreal h = args[3]->NumberValue();
+
+ if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
+ return args.This();
+
+ r->context->buffer()->fillRect(x, y, w, h);
+ }
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::fillRect(real x, real y, real w, real h)
+ Stroke the specified rectangle's path using the strokeStyle, lineWidth, lineJoin,
+ and (if appropriate) miterLimit attributes.
+
+ \sa QtQuick2::Context2D::strokeStyle
+ \sa QtQuick2::Context2D::lineWidth
+ \sa QtQuick2::Context2D::lineJoin
+ \sa QtQuick2::Context2D::miterLimit
+ */
+static v8::Handle<v8::Value> ctx2d_strokeRect(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ if (args.Length() == 4) {
+ qreal x = args[0]->NumberValue();
+ qreal y = args[1]->NumberValue();
+ qreal w = args[2]->NumberValue();
+ qreal h = args[3]->NumberValue();
+
+ if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
+ return args.This();
+
+ r->context->buffer()->strokeRect(x, y, w, h);
+ }
+
+ return args.This();
+}
+
+// Complex shapes (paths) API
+/*!
+ \qmlmethod object QtQuick2::Context2D::arc(real x, real y, real radius, real startAngle, real endAngle, bool anticlockwise)
+ Adds an arc to the current subpath that lies on the circumference of the circle whose center is at the point (\c x,\cy) and whose radius is \c radius.
+ \image qml-item-canvas-arcTo2.png
+ \sa QtQuick2::Context2D::arcTo
+ See {http://www.w3.org/TR/2dcontext/#dom-context-2d-arc}{W3C 2d context standard for arc}
+ */
+static v8::Handle<v8::Value> ctx2d_arc(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ if (args.Length() >= 5) {
+ bool antiClockwise = false;
+
+ if (args.Length() == 6)
+ antiClockwise = args[5]->BooleanValue();
+
+ qreal radius = args[2]->NumberValue();
+ qreal x = args[0]->NumberValue();
+ qreal y = args[1]->NumberValue();
+ qreal sa = args[3]->NumberValue();
+ qreal ea = args[4]->NumberValue();
+
+ if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(sa) || !qIsFinite(ea))
+ return args.This();
+
+ if (radius < 0)
+ V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
+
+ r->context->arc(args[0]->NumberValue(),
+ args[1]->NumberValue(),
+ radius,
+ args[3]->NumberValue(),
+ args[4]->NumberValue(),
+ antiClockwise);
+ }
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::arcTo(real x1, real y1, real x2, real y2, real radius)
+
+ Adds an arc with the given control points and radius to the current subpath, connected to the previous point by a straight line.
+ To draw an arc, you begin with the same steps your followed to create a line:
+ \list
+ \o Call the context.beginPath() method to set a new path.
+ \o Call the context.moveTo(\c x, \c y) method to set your starting position on the canvas at the point (\c x,\c y).
+ \o To draw an arc or circle, call the context.arcTo(\c x1, \c y1, \c x2, \c y2,\c radius) method.
+ This adds an arc with starting point (\c x1,\c y1), ending point (\c x2, \c y2), and radius \c radius to the current subpath and connects
+ it to the previous subpath by a straight line.
+ \endlist
+ \image qml-item-canvas-arcTo.png
+ Both startAngle and endAngle are measured from the x axis in units of radians.
+
+ \image qml-item-canvas-startAngle.png
+ The anticlockwise has the value TRUE for each arc in the figure above because they are all drawn in the counterclockwise direction.
+ \sa QtQuick2::Context2D::arc
+ \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-arcto}{W3C 2d context standard for arcTo}
+ */
+static v8::Handle<v8::Value> ctx2d_arcTo(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+
+ if (args.Length() == 5) {
+ qreal x1 = args[0]->NumberValue();
+ qreal y1 = args[1]->NumberValue();
+ qreal x2 = args[2]->NumberValue();
+ qreal y2 = args[3]->NumberValue();
+
+ if (!qIsFinite(x1) || !qIsFinite(y1) || !qIsFinite(x2) || !qIsFinite(y2))
+ return args.This();
+
+ qreal radius = args[4]->NumberValue();
+ if (radius < 0)
+ V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "Incorrect argument radius");
+ r->context->arcTo(args[0]->NumberValue(),
+ args[1]->NumberValue(),
+ args[2]->NumberValue(),
+ args[3]->NumberValue(),
+ args[4]->NumberValue());
+ }
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::beginPath()
+
+ Resets the current path to a new path.
+ */
+static v8::Handle<v8::Value> ctx2d_beginPath(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ r->context->beginPath();
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::bezierCurveTo(real cp1x, real cp1y, real cp2x, real cp2y, real x, real y)
+
+ Adds a cubic Bezier curve between the current position and the given endPoint using the control points specified by (\c cp1x, cp1y),
+ and (\c cp2x, \c cp2y).
+ After the curve is added, the current position is updated to be at the end point (\c x, \c y) of the curve.
+ The following code produces the path shown below:
+ \code
+ ctx.strokeStyle = Qt.rgba(0, 0, 0, 1);
+ ctx.lineWidth = 1;
+ ctx.beginPath();
+ ctx.moveTo(20, 0);//start point
+ ctx.bezierCurveTo(-10, 90, 210, 90, 180, 0);
+ ctx.stroke();
+ \endcode
+ \image qml-item-canvas-bezierCurveTo.png
+ \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-beziercurveto}{W3C 2d context standard for bezierCurveTo}
+ \sa {http://www.openrise.com/lab/FlowerPower/}{The beautiful flower demo by using bezierCurveTo}
+ */
+static v8::Handle<v8::Value> ctx2d_bezierCurveTo(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ if (args.Length() == 6) {
+ qreal cp1x = args[0]->NumberValue();
+ qreal cp1y = args[1]->NumberValue();
+ qreal cp2x = args[2]->NumberValue();
+ qreal cp2y = args[3]->NumberValue();
+ qreal x = args[4]->NumberValue();
+ qreal y = args[5]->NumberValue();
+
+ if (!qIsFinite(cp1x) || !qIsFinite(cp1y) || !qIsFinite(cp2x) || !qIsFinite(cp2y) || !qIsFinite(x) || !qIsFinite(y))
+ return args.This();
+
+ r->context->bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
+ }
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::clip()
+
+ Creates the clipping region from the current path.
+ Any parts of the shape outside the clipping path are not displayed.
+ To create a complex shape using the \a clip() method:
+
+ \list 1
+ \o Call the \c{context.beginPath()} method to set the clipping path.
+ \o Define the clipping path by calling any combination of the \c{lineTo},
+ \c{arcTo}, \c{arc}, \c{moveTo}, etc and \c{closePath} methods.
+ \o Call the \c{context.clip()} method.
+ \endlist
+
+ The new shape displays. The following shows how a clipping path can
+ modify how an image displays:
+
+ \image qml-canvas-clip-complex.png
+ \sa QtQuick2::Context2D::beginPath()
+ \sa QtQuick2::Context2D::closePath()
+ \sa QtQuick2::Context2D::stroke()
+ \sa QtQuick2::Context2D::fill()
+ \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-clip}{W3C 2d context standard for clip}
+ */
+static v8::Handle<v8::Value> ctx2d_clip(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ QPainterPath clipPath = r->context->m_path;
+ clipPath.closeSubpath();
+ if (!r->context->state.clipPath.isEmpty())
+ r->context->state.clipPath = clipPath.intersected(r->context->state.clipPath);
+ else
+ r->context->state.clipPath = clipPath;
+ r->context->buffer()->clip(r->context->state.clipPath);
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::closePath()
+ Closes the current subpath by drawing a line to the beginning of the subpath, automatically starting a new path.
+ The current point of the new path is the previous subpath's first point.
+
+ \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-closepath}{W3C 2d context standard for closePath}
+ */
+static v8::Handle<v8::Value> ctx2d_closePath(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ r->context->closePath();
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::fill()
+
+ Fills the subpaths with the current fill style.
+
+ \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-fill}{W3C 2d context standard for fill}
+
+ \sa QtQuick2::Context2D::fillStyle
+ */
+static v8::Handle<v8::Value> ctx2d_fill(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r);
+
+ r->context->buffer()->fill(r->context->m_path);
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::lineTo(real x, real y)
+
+ Draws a line from the current position to the point (x, y).
+ */
+static v8::Handle<v8::Value> ctx2d_lineTo(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ if (args.Length() == 2) {
+ qreal x = args[0]->NumberValue();
+ qreal y = args[1]->NumberValue();
+
+ if (!qIsFinite(x) || !qIsFinite(y))
+ return args.This();
+
+ r->context->lineTo(x, y);
+ }
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::moveTo(real x, real y)
+
+ Creates a new subpath with the given point.
+ */
+static v8::Handle<v8::Value> ctx2d_moveTo(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ if (args.Length() == 2) {
+ qreal x = args[0]->NumberValue();
+ qreal y = args[1]->NumberValue();
+
+ if (!qIsFinite(x) || !qIsFinite(y))
+ return args.This();
+ r->context->moveTo(x, y);
+ }
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::quadraticCurveTo(real cpx, real cpy, real x, real y)
+
+ Adds a quadratic Bezier curve between the current point and the endpoint (\c x, \c y) with the control point specified by (\c cpx, \c cpy).
+
+ See {http://www.w3.org/TR/2dcontext/#dom-context-2d-quadraticcurveto}{W3C 2d context standard for for quadraticCurveTo}
+ */
+static v8::Handle<v8::Value> ctx2d_quadraticCurveTo(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ if (args.Length() == 4) {
+ qreal cpx = args[0]->NumberValue();
+ qreal cpy = args[1]->NumberValue();
+ qreal x = args[2]->NumberValue();
+ qreal y = args[3]->NumberValue();
+
+ if (!qIsFinite(cpx) || !qIsFinite(cpy) || !qIsFinite(x) || !qIsFinite(y))
+ return args.This();
+
+ r->context->quadraticCurveTo(cpx, cpy, x, y);
+ }
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::rect(real x, real y, real w, real h)
+
+ Adds a rectangle at position (\c x, \c y), with the given width \c w and height \c h, as a closed subpath.
+ */
+static v8::Handle<v8::Value> ctx2d_rect(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ if (args.Length() == 4) {
+ qreal x = args[0]->NumberValue();
+ qreal y = args[1]->NumberValue();
+ qreal w = args[2]->NumberValue();
+ qreal h = args[3]->NumberValue();
+
+ if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
+ return args.This();
+
+ r->context->rect(x, y, w, h);
+ }
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::roundedRect(real x, real y, real w, real h, real xRadius, real yRadius)
+
+ Adds the given rectangle rect with rounded corners to the path. The \c xRadius and \c yRadius arguments specify the radius of the
+ ellipses defining the corners of the rounded rectangle.
+ */
+static v8::Handle<v8::Value> ctx2d_roundedRect(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ if (args.Length() == 6) {
+ qreal x = args[0]->NumberValue();
+ qreal y = args[1]->NumberValue();
+ qreal w = args[2]->NumberValue();
+ qreal h = args[3]->NumberValue();
+ qreal xr = args[4]->NumberValue();
+ qreal yr = args[5]->NumberValue();
+
+ if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
+ return args.This();
+
+ if (!qIsFinite(xr) || !qIsFinite(yr))
+ V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "roundedRect(): Invalid arguments");
+
+ r->context->roundedRect(x, y, w, h, xr, yr);
+ }
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::ellipse(real x, real y, real w, real h)
+
+ Creates an ellipse within the bounding rectangle defined by its top-left corner at (\a x, \ y), width \a w and height \a h,
+ and adds it to the path as a closed subpath.
+
+ The ellipse is composed of a clockwise curve, starting and finishing at zero degrees (the 3 o'clock position).
+ */
+static v8::Handle<v8::Value> ctx2d_ellipse(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ if (args.Length() == 4) {
+ qreal x = args[0]->NumberValue();
+ qreal y = args[1]->NumberValue();
+ qreal w = args[2]->NumberValue();
+ qreal h = args[3]->NumberValue();
+
+ if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(h))
+ return args.This();
+
+
+ r->context->ellipse(x, y, w, h);
+ }
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::text(string text, real x, real y)
+
+ Adds the given \c text to the path as a set of closed subpaths created from the current context font supplied.
+ The subpaths are positioned so that the left end of the text's baseline lies at the point specified by (\c x, \c y).
+ */
+static v8::Handle<v8::Value> ctx2d_text(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ QV8Engine *engine = V8ENGINE();
+ if (args.Length() == 3) {
+ qreal x = args[1]->NumberValue();
+ qreal y = args[2]->NumberValue();
+
+ if (!qIsFinite(x) || !qIsFinite(y))
+ return args.This();
+ r->context->text(engine->toString(args[0]), x, y);
+ }
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::stroke()
+
+ Strokes the subpaths with the current stroke style.
+
+ See {http://www.w3.org/TR/2dcontext/#dom-context-2d-stroke}{W3C 2d context standard for stroke}
+
+ \sa QtQuick2::Context2D::strokeStyle
+ */
+static v8::Handle<v8::Value> ctx2d_stroke(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ r->context->buffer()->stroke(r->context->m_path);
+
+ return args.This();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::isPointInPath(real x, real y)
+
+ Returns true if the given point is in the current path.
+
+ \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-ispointinpath}{W3C 2d context standard for isPointInPath}
+ */
+static v8::Handle<v8::Value> ctx2d_isPointInPath(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ bool pointInPath = false;
+ if (args.Length() == 2) {
+ qreal x = args[0]->NumberValue();
+ qreal y = args[1]->NumberValue();
+ if (!qIsFinite(x) || !qIsFinite(y))
+ return v8::Boolean::New(false);
+ pointInPath = r->context->isPointInPath(x, y);
+ }
+ return v8::Boolean::New(pointInPath);
+}
+
+static v8::Handle<v8::Value> ctx2d_drawFocusRing(const v8::Arguments &args)
+{
+ V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::drawFocusRing is not supported");
+ return args.This();
+}
+
+static v8::Handle<v8::Value> ctx2d_setCaretSelectionRect(const v8::Arguments &args)
+{
+ V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::setCaretSelectionRect is not supported");
+ return args.This();
+}
+
+static v8::Handle<v8::Value> ctx2d_caretBlinkRate(const v8::Arguments &args)
+{
+ V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "Context2D::caretBlinkRate is not supported");
+ return args.This();
+}
+// text
+/*!
+ \qmlproperty string QtQuick2::Context2D::font
+ Holds the current font settings.
+
+ The default font value is "10px sans-serif".
+ See {http://www.w3.org/TR/2dcontext/#dom-context-2d-font}{w3C 2d context standard for font}
+ */
+v8::Handle<v8::Value> ctx2d_font(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+
+ return engine->toString(r->context->state.font.toString());
+}
+
+static void ctx2d_font_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT_SETTER(r)
+
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+ QString fs = engine->toString(value);
+ QFont font = qt_font_from_string(fs);
+ if (font != r->context->state.font) {
+ r->context->state.font = font;
+ }
+}
+
+/*!
+ \qmlproperty string QtQuick2::Context2D::textAlign
+
+ Holds the current text alignment settings.
+ The possible values are:
+ \list
+ \o start
+ \o end
+ \o left
+ \o right
+ \o center
+ \endlist
+ Other values are ignored. The default value is "start".
+ */
+v8::Handle<v8::Value> ctx2d_textAlign(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+ switch (r->context->state.textAlign) {
+ case QQuickContext2D::Start:
+ return engine->toString(QLatin1String("start"));
+ case QQuickContext2D::End:
+ return engine->toString(QLatin1String("end"));
+ case QQuickContext2D::Left:
+ return engine->toString(QLatin1String("left"));
+ case QQuickContext2D::Right:
+ return engine->toString(QLatin1String("right"));
+ case QQuickContext2D::Center:
+ return engine->toString(QLatin1String("center"));
+ default:
+ break;
+ }
+ return engine->toString(QLatin1String("start"));
+}
+
+static void ctx2d_textAlign_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT_SETTER(r)
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+
+ QString textAlign = engine->toString(value);
+
+ QQuickContext2D::TextAlignType ta;
+ if (textAlign == QLatin1String("start"))
+ ta = QQuickContext2D::Start;
+ else if (textAlign == QLatin1String("end"))
+ ta = QQuickContext2D::End;
+ else if (textAlign == QLatin1String("left"))
+ ta = QQuickContext2D::Left;
+ else if (textAlign == QLatin1String("right"))
+ ta = QQuickContext2D::Right;
+ else if (textAlign == QLatin1String("center"))
+ ta = QQuickContext2D::Center;
+ else
+ return;
+
+ if (ta != r->context->state.textAlign) {
+ r->context->state.textAlign = ta;
+ }
+}
+
+/*!
+ \qmlproperty string QtQuick2::Context2D::textBaseline
+
+ Holds the current baseline alignment settings.
+ The possible values are:
+ \list
+ \o top
+ \o hanging
+ \o middle
+ \o alphabetic
+ \o ideographic
+ \o bottom
+ \endlist
+ Other values are ignored. The default value is "alphabetic".
+ */
+v8::Handle<v8::Value> ctx2d_textBaseline(v8::Local<v8::String>, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT(r)
+
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+ switch (r->context->state.textBaseline) {
+ case QQuickContext2D::Alphabetic:
+ return engine->toString(QLatin1String("alphabetic"));
+ case QQuickContext2D::Hanging:
+ return engine->toString(QLatin1String("hanging"));
+ case QQuickContext2D::Top:
+ return engine->toString(QLatin1String("top"));
+ case QQuickContext2D::Bottom:
+ return engine->toString(QLatin1String("bottom"));
+ case QQuickContext2D::Middle:
+ return engine->toString(QLatin1String("middle"));
+ default:
+ break;
+ }
+ return engine->toString(QLatin1String("alphabetic"));
+}
+
+static void ctx2d_textBaseline_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ CHECK_CONTEXT_SETTER(r)
+ QV8Engine *engine = V8ENGINE_ACCESSOR();
+ QString textBaseline = engine->toString(value);
+
+ QQuickContext2D::TextBaseLineType tb;
+ if (textBaseline == QLatin1String("alphabetic"))
+ tb = QQuickContext2D::Alphabetic;
+ else if (textBaseline == QLatin1String("hanging"))
+ tb = QQuickContext2D::Hanging;
+ else if (textBaseline == QLatin1String("top"))
+ tb = QQuickContext2D::Top;
+ else if (textBaseline == QLatin1String("bottom"))
+ tb = QQuickContext2D::Bottom;
+ else if (textBaseline == QLatin1String("middle"))
+ tb = QQuickContext2D::Middle;
+ else
+ return;
+
+ if (tb != r->context->state.textBaseline) {
+ r->context->state.textBaseline = tb;
+ }
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::fillText(text, x, y)
+ Fills the given text at the given position.
+ \sa QtQuick2::Context2D::font
+ \sa QtQuick2::Context2D::textAlign
+ \sa QtQuick2::Context2D::textBaseline
+ \sa QtQuick2::Context2D::strokeText
+ */
+static v8::Handle<v8::Value> ctx2d_fillText(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ QV8Engine *engine = V8ENGINE();
+ if (args.Length() == 3) {
+ qreal x = args[1]->NumberValue();
+ qreal y = args[2]->NumberValue();
+ if (!qIsFinite(x) || !qIsFinite(y))
+ return args.This();
+ QPainterPath textPath = r->context->createTextGlyphs(x, y, engine->toString(args[0]));
+ r->context->buffer()->fill(textPath);
+ }
+ return args.This();
+}
+/*!
+ \qmlmethod object QtQuick2::Context2D::strokeText(text, x, y)
+ Strokes the given text at the given position.
+ \sa QtQuick2::Context2D::font
+ \sa QtQuick2::Context2D::textAlign
+ \sa QtQuick2::Context2D::textBaseline
+ \sa QtQuick2::Context2D::fillText
+ */
+static v8::Handle<v8::Value> ctx2d_strokeText(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ QV8Engine *engine = V8ENGINE();
+ if (args.Length() == 3) {
+ qreal x = args[1]->NumberValue();
+ qreal y = args[2]->NumberValue();
+ if (!qIsFinite(x) || !qIsFinite(y))
+ return args.This();
+ QPainterPath textPath = r->context->createTextGlyphs(x, y, engine->toString(args[0]));
+ r->context->buffer()->stroke(textPath);
+ }
+ return args.This();
+}
+/*!
+ \qmlclass QtQuick2::TextMetrics
+ \inqmlmodule QtQuick 2
+ \since QtQuick 2.0
+ \brief The Context2D TextMetrics interface.
+ The TextMetrics object can be created by QtQuick2::Context2D::measureText method.
+ See {http://www.w3.org/TR/2dcontext/#textmetrics}{W3C 2d context TexMetrics} for more details.
+
+ \sa QtQuick2::Context2D::measureText
+ \sa QtQuick2::TextMetrics::width
+ */
+
+/*!
+ \qmlproperty int QtQuick2::TextMetrics::width
+ Holds the advance width of the text that was passed to the QtQuick2::Context2D::measureText() method.
+ This property is read only.
+ */
+
+/*!
+ \qmlmethod variant QtQuick2::Context2D::measureText(text)
+ Returns a TextMetrics object with the metrics of the given text in the current font.
+ */
+static v8::Handle<v8::Value> ctx2d_measureText(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ QV8Engine *engine = V8ENGINE();
+
+ if (args.Length() == 1) {
+ QFontMetrics fm(r->context->state.font);
+ uint width = fm.width(engine->toString(args[0]));
+ v8::Local<v8::Object> tm = v8::Object::New();
+ tm->Set(v8::String::New("width"), v8::Number::New(width));
+ return tm;
+ }
+ return v8::Undefined();
+}
+
+// drawing images
+/*!
+ \qmlmethod QtQuick2::Context2D::drawImage(variant image, real dx, real dy)
+ Draws the given \a image on the canvas at position (\a dx, \a dy).
+ Note:
+ The \a image type can be an Image item, an image url or a \a {QtQuick2::CanvasImageData} object.
+ When given as Image item, if the image isn't fully loaded, this method draws nothing.
+ When given as url string, the image should be loaded by calling Canvas item's \a QtQuick2::Canvas::loadImage() method first.
+ This image been drawing is subject to the current context clip path, even the given \c image is a {QtQuick2::CanvasImageData} object.
+
+ \sa QtQuick2::CanvasImageData
+ \sa QtQuick2::Image
+ \sa QtQuick2::Canvas::loadImage
+ \sa QtQuick2::Canvas::isImageLoaded
+ \sa QtQuick2::Canvas::imageLoaded
+
+ \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
+ */
+/*!
+ \qmlmethod QtQuick2::Context2D::drawImage(variant image, real dx, real dy, real dw, real dh)
+ This is an overloaded function.
+ Draws the given item as \a image onto the canvas at point (\a dx, \a dy) and with width \a dw,
+ height \a dh.
+
+ Note:
+ The \a image type can be an Image item, an image url or a \a {QtQuick2::CanvasImageData} object.
+ When given as Image item, if the image isn't fully loaded, this method draws nothing.
+ When given as url string, the image should be loaded by calling Canvas item's \a QtQuick2::Canvas::loadImage() method first.
+ This image been drawing is subject to the current context clip path, even the given \c image is a {QtQuick2::CanvasImageData} object.
+
+ \sa QtQuick2::CanvasImageData
+ \sa QtQuick2::Image
+ \sa QtQuick2::Canvas::loadImage
+ \sa QtQuick2::Canvas::isImageLoaded
+ \sa QtQuick2::Canvas::imageLoaded
+
+ \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
+ */
+/*!
+ \qmlmethod QtQuick2::Context2D::drawImage(variant image, real sx, real sy, real sw, sh, real dx, real dy, real dw, dh)
+ This is an overloaded function.
+ Draws the given item as \a image from source point (\a sx, \a sy) and source width \sw, source height \sh
+ onto the canvas at point (\a dx, \a dy) and with width \a dw, height \a dh.
+
+
+ Note:
+ The \a image type can be an Image or Canvas item, an image url or a \a {QtQuick2::CanvasImageData} object.
+ When given as Image item, if the image isn't fully loaded, this method draws nothing.
+ When given as url string, the image should be loaded by calling Canvas item's \a QtQuick2::Canvas::loadImage() method first.
+ This image been drawing is subject to the current context clip path, even the given \c image is a {QtQuick2::CanvasImageData} object.
+
+ \sa QtQuick2::CanvasImageData
+ \sa QtQuick2::Image
+ \sa QtQuick2::Canvas::loadImage
+ \sa QtQuick2::Canvas::isImageLoaded
+ \sa QtQuick2::Canvas::imageLoaded
+
+ \sa {http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage}{W3C 2d context standard for drawImage}
+*/
+static v8::Handle<v8::Value> ctx2d_drawImage(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ QV8Engine *engine = V8ENGINE();
+ qreal sx, sy, sw, sh, dx, dy, dw, dh;
+
+ if (!args.Length())
+ return args.This();
+
+ QImage image;
+ if (args[0]->IsString()) {
+ QUrl url(engine->toString(args[0]->ToString()));
+ if (!url.isValid())
+ V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
+
+ image = r->context->createImage(url);
+ } else if (args[0]->IsObject()) {
+ QQuickImage *imageItem = qobject_cast<QQuickImage*>(engine->toQObject(args[0]->ToObject()));
+ QQuickCanvasItem *canvas = qobject_cast<QQuickCanvasItem*>(engine->toQObject(args[0]->ToObject()));
+
+ QV8Context2DPixelArrayResource *pix = v8_resource_cast<QV8Context2DPixelArrayResource>(args[0]->ToObject()->GetInternalField(0)->ToObject());
+ if (pix) {
+ image = pix->image;
+ } else if (imageItem) {
+ image = imageItem->image();
+ } else if (canvas) {
+ image = canvas->toImage();
+ } else {
+ V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
+ }
+ } else {
+ V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "drawImage(), type mismatch");
+ }
+ if (args.Length() == 3) {
+ dx = args[1]->NumberValue();
+ dy = args[2]->NumberValue();
+ sx = 0;
+ sy = 0;
+ sw = image.width();
+ sh = image.height();
+ dw = sw;
+ dh = sh;
+ } else if (args.Length() == 5) {
+ sx = 0;
+ sy = 0;
+ sw = image.width();
+ sh = image.height();
+ dx = args[1]->NumberValue();
+ dy = args[2]->NumberValue();
+ dw = args[3]->NumberValue();
+ dh = args[4]->NumberValue();
+ } else if (args.Length() == 9) {
+ sx = args[1]->NumberValue();
+ sy = args[2]->NumberValue();
+ sw = args[3]->NumberValue();
+ sh = args[4]->NumberValue();
+ dx = args[5]->NumberValue();
+ dy = args[6]->NumberValue();
+ dw = args[7]->NumberValue();
+ dh = args[8]->NumberValue();
+ } else {
+ return args.This();
+ }
+
+ if (!qIsFinite(sx)
+ || !qIsFinite(sy)
+ || !qIsFinite(sw)
+ || !qIsFinite(sh)
+ || !qIsFinite(dx)
+ || !qIsFinite(dy)
+ || !qIsFinite(dw)
+ || !qIsFinite(dh))
+ return args.This();
+
+ if (!image.isNull()) {
+ if (sx < 0 || sy < 0 || sw == 0 || sh == 0
+ || sx + sw > image.width() || sy + sh > image.height()
+ || sx + sw < 0 || sy + sh < 0) {
+ V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "drawImage(), index size error");
+ }
+
+ r->context->buffer()->drawImage(image,sx, sy, sw, sh, dx, dy, dw, dh);
+ }
+
+ return args.This();
+}
+
+// pixel manipulation
+/*!
+ \qmlclass QtQuick2::CanvasImageData
+ The \a QtQuick2::CanvasImageData object holds the image pixel data.
+
+ The \a QtQuick2::CanvasImageData object has the actual dimensions of the data stored in
+ this object and holds the one-dimensional array containing the data in RGBA order,
+ as integers in the range 0 to 255.
+
+ \sa QtQuick2::CanvasImageData::width
+ \sa QtQuick2::CanvasImageData::height
+ \sa QtQuick2::CanvasImageData::data
+ \sa QtQuick2::Context2D::createImageData
+ \sa QtQuick2::Context2D::getImageData
+ \sa QtQuick2::Context2D::putImageData
+ */
+/*!
+ \qmlproperty QtQuick2::CanvasImageData::width
+ Holds the actual width dimension of the data in the ImageData object, in device pixels.
+ */
+v8::Handle<v8::Value> ctx2d_imageData_width(v8::Local<v8::String>, const v8::AccessorInfo &args)
+{
+ QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This()->GetInternalField(0)->ToObject());
+ if (!r)
+ return v8::Integer::New(0);
+ return v8::Integer::New(r->image.width());
+}
+
+/*!
+ \qmlproperty QtQuick2::CanvasImageData::height
+ Holds the actual height dimension of the data in the ImageData object, in device pixels.
+ */
+v8::Handle<v8::Value> ctx2d_imageData_height(v8::Local<v8::String>, const v8::AccessorInfo &args)
+{
+ QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This()->GetInternalField(0)->ToObject());
+ if (!r)
+ return v8::Integer::New(0);
+
+ return v8::Integer::New(r->image.height());
+}
+
+/*!
+ \qmlproperty QtQuick2::CanvasImageData::data
+ Holds the one-dimensional array containing the data in RGBA order, as integers in the range 0 to 255.
+ */
+v8::Handle<v8::Value> ctx2d_imageData_data(v8::Local<v8::String>, const v8::AccessorInfo &args)
+{
+ return args.This()->GetInternalField(0);
+}
+
+/*!
+ \qmlmethod void QtQuick2::CanvasImageData::mirrr( bool horizontal = false, bool vertical = true)
+ Mirrors the image data in place in the \c horizontal and/or the \c vertical direction depending on
+ whether horizontal and vertical are set to true or false.
+ The default \c horizontal value is false, the default \c vertical value is true.
+*/
+static v8::Handle<v8::Value> ctx2d_imageData_mirror(const v8::Arguments &args)
+{
+ bool horizontal = false, vertical = true;
+ QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This()->GetInternalField(0)->ToObject());
+
+ if (!r) {
+ //error
+ return v8::Undefined();
+ }
+
+ if (args.Length() > 2) {
+ //error
+ return v8::Undefined();
+ }
+
+ if (args.Length() == 1) {
+ horizontal = args[0]->BooleanValue();
+ } else if (args.Length() == 2) {
+ horizontal = args[0]->BooleanValue();
+ vertical = args[1]->BooleanValue();
+ }
+ r->image = r->image.mirrored(horizontal, vertical);
+ return args.This();
+}
+
+/*!
+ \qmlmethod void QtQuick2::CanvasImageData::filter(enumeration mode, args)
+ Filters the image data as defined by one of the following modes:
+ \list
+ \o Canvas.Threshold - converts the image to black and white pixels depending
+ if they are above or below the threshold defined by the level parameter.
+ The level must be between 0.0 (black) and 1.0(white).
+ If no level is specified, 0.5 is used.
+ \o Canvas.Mono - converts the image to the 1-bit per pixel format.
+ \o Canvas.GrayScale - converts any colors in the image to grayscale equivalents.
+ \o Canvas.Brightness -increase/decrease a fixed \c adjustment value to each pixel's RGB channel value.
+ \o Canvas.Invert - sets each pixel to its inverse value.
+ \o Canvas.Blur - executes a box blur with the pixel \c radius parameter specifying the range of the blurring for each pixel.
+ the default blur \c radius is 3. This filter also accepts another \c quality parameter, if true, the filter will
+ execute 3-passes box blur to simulate the Guassian blur. The default \c quality value is false.
+ \o Canvas.Opaque - sets the alpha channel to entirely opaque.
+ \o Canvas.Convolute - executes a generic {http://en.wikipedia.org/wiki/Convolution}{Convolution} filter, the second
+ parameter contains the convoluton matrix data as a number array.
+ \endlist
+
+*/
+static v8::Handle<v8::Value> ctx2d_imageData_filter(const v8::Arguments &args)
+{
+ QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This()->GetInternalField(0)->ToObject());
+
+ if (!r) {
+ //error
+ return v8::Undefined();
+ }
+
+ if (args.Length() >= 1) {
+ int filterFlag = args[0]->IntegerValue();
+ switch (filterFlag) {
+ case QQuickCanvasItem::Mono :
+ {
+ r->image = r->image.convertToFormat(QImage::Format_Mono).convertToFormat(QImage::Format_ARGB32_Premultiplied);
+ }
+ break;
+ case QQuickCanvasItem::GrayScale :
+ {
+ for (int y = 0; y < r->image.height(); ++y) {
+ QRgb *row = (QRgb*)r->image.scanLine(y);
+ for (int x = 0; x < r->image.width(); ++x) {
+ unsigned char* rgb = ((unsigned char*)&row[x]);
+ rgb[0] = rgb[1] = rgb[2] = qGray(rgb[0], rgb[1], rgb[2]);
+ }
+ }
+ }
+ break;
+ case QQuickCanvasItem::Threshold :
+ {
+ qreal threshold = 0.5;
+ if (args.Length() > 1)
+ threshold = args[1]->NumberValue();
+
+ for (int y = 0; y < r->image.height(); ++y) {
+ QRgb *row = (QRgb*)r->image.scanLine(y);
+ for (int x = 0; x < r->image.width(); ++x) {
+ unsigned char* rgb = ((unsigned char*)&row[x]);
+ unsigned char v = qGray(rgb[0], rgb[1], rgb[2]) >= threshold*255 ? 255 : 0;
+ rgb[0] = rgb[1] = rgb[2] = v;
+ }
+ }
+ }
+ break;
+ case QQuickCanvasItem::Brightness :
+ {
+ int adjustment = 1;
+ if (args.Length() > 1)
+ adjustment = args[1]->IntegerValue();
+
+ for (int y = 0; y < r->image.height(); ++y) {
+ QRgb *row = (QRgb*)r->image.scanLine(y);
+ for (int x = 0; x < r->image.width(); ++x) {
+ ((unsigned char*)&row[x])[0] += adjustment;
+ ((unsigned char*)&row[x])[1] += adjustment;
+ ((unsigned char*)&row[x])[2] += adjustment;
+ }
+ }
+ }
+ break;
+ case QQuickCanvasItem::Invert :
+ {
+ r->image.invertPixels();
+ }
+ break;
+ case QQuickCanvasItem::Blur :
+ {
+ int radius = 3;
+ bool quality = false;
+
+ if (args.Length() > 1)
+ radius = args[1]->IntegerValue() / 2;
+ if (args.Length() > 2)
+ quality = args[2]->BooleanValue();
+
+ qt_image_boxblur(r->image, radius, quality);
+ }
+ break;
+ case QQuickCanvasItem::Opaque :
+ {
+ for (int y = 0; y < r->image.height(); ++y) {
+ QRgb *row = (QRgb*)r->image.scanLine(y);
+ for (int x = 0; x < r->image.width(); ++x) {
+ ((unsigned char*)&row[x])[3] = 255;
+ }
+ }
+ }
+ break;
+ case QQuickCanvasItem::Convolute :
+ {
+ if (args.Length() > 1 && args[1]->IsArray()) {
+ v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(args[1]);
+ QVector<qreal> weights;
+ for (uint32_t i = 0; i < array->Length(); ++i)
+ weights.append(array->Get(i)->NumberValue());
+ r->image = qt_image_convolute_filter(r->image, weights);
+ } else {
+ //error
+ }
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return args.This();
+}
+/*!
+ \qmlclass QtQuick2::CanvasPixelArray
+ The CanvasPixelArray object provides ordered, indexed access to the color components of each pixel of the image data.
+ The CanvasPixelArray can be accessed as normal Javascript array.
+ \sa QtQuick2::CanvasImageData
+ \sa {http://www.w3.org/TR/2dcontext/#canvaspixelarray}{W3C 2d context standard for PixelArray}
+ */
+
+/*!
+ \qmlproperty QtQuick2::CanvasPixelArray::length
+ The CanvasPixelArray object represents h×w×4 integers which w and h comes from CanvasImageData.
+ The length attribute of a CanvasPixelArray object must return this h×w×4 number value.
+ This property is read only.
+*/
+v8::Handle<v8::Value> ctx2d_pixelArray_length(v8::Local<v8::String>, const v8::AccessorInfo &args)
+{
+ QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This());
+ if (!r || r->image.isNull()) return v8::Undefined();
+
+ return v8::Integer::New(r->image.width() * r->image.height() * 4);
+}
+
+v8::Handle<v8::Value> ctx2d_pixelArray_indexed(uint32_t index, const v8::AccessorInfo& args)
+{
+ QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This());
+
+ if (r && index < static_cast<quint32>(r->image.width() * r->image.height() * 4)) {
+ const quint32 w = r->image.width();
+ const quint32 row = (index / 4) / w;
+ const quint32 col = (index / 4) % w;
+ const QRgb* pixel = reinterpret_cast<const QRgb*>(r->image.constScanLine(row));
+ pixel += col;
+ switch (index % 4) {
+ case 0:
+ return v8::Integer::New(qRed(*pixel));
+ case 1:
+ return v8::Integer::New(qGreen(*pixel));
+ case 2:
+ return v8::Integer::New(qBlue(*pixel));
+ case 3:
+ return v8::Integer::New(qAlpha(*pixel));
+ }
+ }
+ return v8::Undefined();
+}
+
+v8::Handle<v8::Value> ctx2d_pixelArray_indexed_set(uint32_t index, v8::Local<v8::Value> value, const v8::AccessorInfo& info)
+{
+ QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(info.This());
+
+ const int v = value->Uint32Value();
+ if (r && index < static_cast<quint32>(r->image.width() * r->image.height() * 4) && v > 0 && v <= 255) {
+ const quint32 w = r->image.width();
+ const quint32 row = (index / 4) / w;
+ const quint32 col = (index / 4) % w;
+
+ QRgb* pixel = reinterpret_cast<QRgb*>(r->image.scanLine(row));
+ pixel += col;
+ switch (index % 4) {
+ case 0:
+ *pixel = qRgba(v, qGreen(*pixel), qBlue(*pixel), qAlpha(*pixel));
+ break;
+ case 1:
+ *pixel = qRgba(qRed(*pixel), v, qBlue(*pixel), qAlpha(*pixel));
+ break;
+ case 2:
+ *pixel = qRgba(qRed(*pixel), qGreen(*pixel), v, qAlpha(*pixel));
+ break;
+ case 3:
+ *pixel = qRgba(qRed(*pixel), qGreen(*pixel), qBlue(*pixel), v);
+ break;
+ }
+ }
+ return v8::Undefined();
+}
+/*!
+ \qmlmethod QtQuick2::CanvasImageData createImageData(real sw, real sh)
+ Creates a CanvasImageData object with the given dimensions(\a sw, \a sh).
+ */
+/*!
+ \qmlmethod QtQuick2::CanvasImageData createImageData(QtQuick2::CanvasImageData imageData)
+ Creates a CanvasImageData object with the same dimensions as the argument.
+ */
+/*!
+ \qmlmethod QtQuick2::CanvasImageData createImageData(Url imageUrl)
+ Creates a CanvasImageData object with the given image loaded from \a imageUrl.
+ Note:The \a imageUrl must be already loaded before this function call, if not, an empty
+ CanvasImageData obect will be returned.
+
+ \sa QtQuick2::Canvas::loadImage, QtQuick2::Canvas::unloadImage, QtQuick2::Canvas::isImageLoaded
+ */
+static v8::Handle<v8::Value> ctx2d_createImageData(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ QV8Engine *engine = V8ENGINE();
+
+ if (args.Length() == 1) {
+ if (args[0]->IsObject()) {
+ v8::Local<v8::Object> imgData = args[0]->ToObject();
+ QV8Context2DPixelArrayResource *pa = v8_resource_cast<QV8Context2DPixelArrayResource>(imgData->GetInternalField(0)->ToObject());
+ if (pa) {
+ qreal w = imgData->Get(v8::String::New("width"))->NumberValue();
+ qreal h = imgData->Get(v8::String::New("height"))->NumberValue();
+ return qt_create_image_data(w, h, engine, QImage());
+ }
+ } else if (args[0]->IsString()) {
+ QImage image = r->context->createImage(QUrl(engine->toString(args[0]->ToString())));
+ return qt_create_image_data(image.width(), image.height(), engine, image);
+ }
+ } else if (args.Length() == 2) {
+ qreal w = args[0]->NumberValue();
+ qreal h = args[1]->NumberValue();
+
+ if (!qIsFinite(w) || !qIsFinite(h))
+ V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "createImageData(): invalid arguments");
+
+ if (w > 0 && h > 0)
+ return qt_create_image_data(w, h, engine, QImage());
+ else
+ V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "createImageData(): invalid arguments");
+ }
+ return v8::Undefined();
+}
+
+/*!
+ \qmlmethod QtQuick2::CanvasImageData getImageData(real sx, real sy, real sw, real sh)
+ Returns an CanvasImageData object containing the image data for the given rectangle of the canvas.
+ */
+static v8::Handle<v8::Value> ctx2d_getImageData(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ QV8Engine *engine = V8ENGINE();
+ if (args.Length() == 4) {
+ qreal x = args[0]->NumberValue();
+ qreal y = args[1]->NumberValue();
+ qreal w = args[2]->NumberValue();
+ qreal h = args[3]->NumberValue();
+ if (!qIsFinite(x) || !qIsFinite(y) || !qIsFinite(w) || !qIsFinite(w))
+ V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "getImageData(): Invalid arguments");
+
+ if (w <= 0 || h <= 0)
+ V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "getImageData(): Invalid arguments");
+
+ QImage image = r->context->canvas()->toImage(QRectF(x, y, w, h));
+ v8::Local<v8::Object> imageData = qt_create_image_data(w, h, engine, image);
+
+ return imageData;
+ }
+ return v8::Null();
+}
+
+/*!
+ \qmlmethod object QtQuick2::Context2D::putImageData(QtQuick2::CanvasImageData imageData, real dx, real dy, real dirtyX, real dirtyY, real dirtyWidth, real dirtyHeight)
+ Paints the data from the given ImageData object onto the canvas. If a dirty rectangle (\a dirtyX, \a dirtyY, \a dirtyWidth, \a dirtyHeight) is provided, only the pixels from that rectangle are painted.
+ */
+static v8::Handle<v8::Value> ctx2d_putImageData(const v8::Arguments &args)
+{
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+ if (args.Length() != 3 && args.Length() != 7)
+ return v8::Undefined();
+
+ if (args[0]->IsNull() || !args[0]->IsObject()) {
+ V8THROW_DOM(DOMEXCEPTION_TYPE_MISMATCH_ERR, "Context2D::putImageData, the image data type mismatch");
+ }
+ qreal dx = args[1]->NumberValue();
+ qreal dy = args[2]->NumberValue();
+ qreal w, h, dirtyX, dirtyY, dirtyWidth, dirtyHeight;
+
+ if (!qIsFinite(dx) || !qIsFinite(dy))
+ V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments");
+
+ v8::Local<v8::Object> imageData = args[0]->ToObject();
+ QV8Context2DPixelArrayResource *pixelArray = v8_resource_cast<QV8Context2DPixelArrayResource>(imageData->Get(v8::String::New("data"))->ToObject());
+ if (pixelArray) {
+ w = imageData->Get(v8::String::New("width"))->NumberValue();
+ h = imageData->Get(v8::String::New("height"))->NumberValue();
+
+ if (args.Length() == 7) {
+ dirtyX = args[3]->NumberValue();
+ dirtyY = args[4]->NumberValue();
+ dirtyWidth = args[5]->NumberValue();
+ dirtyHeight = args[6]->NumberValue();
+
+ if (!qIsFinite(dirtyX) || !qIsFinite(dirtyY) || !qIsFinite(dirtyWidth) || !qIsFinite(dirtyHeight))
+ V8THROW_DOM(DOMEXCEPTION_NOT_SUPPORTED_ERR, "putImageData() : Invalid arguments");
+
+
+ if (dirtyWidth < 0) {
+ dirtyX = dirtyX+dirtyWidth;
+ dirtyWidth = -dirtyWidth;
+ }
+
+ if (dirtyHeight < 0) {
+ dirtyY = dirtyY+dirtyHeight;
+ dirtyHeight = -dirtyHeight;
+ }
+
+ if (dirtyX < 0) {
+ dirtyWidth = dirtyWidth+dirtyX;
+ dirtyX = 0;
+ }
+
+ if (dirtyY < 0) {
+ dirtyHeight = dirtyHeight+dirtyY;
+ dirtyY = 0;
+ }
+
+ if (dirtyX+dirtyWidth > w) {
+ dirtyWidth = w - dirtyX;
+ }
+
+ if (dirtyY+dirtyHeight > h) {
+ dirtyHeight = h - dirtyY;
+ }
+
+ if (dirtyWidth <=0 || dirtyHeight <= 0)
+ return args.This();
+ } else {
+ dirtyX = 0;
+ dirtyY = 0;
+ dirtyWidth = w;
+ dirtyHeight = h;
+ }
+
+ QImage image = pixelArray->image.copy(dirtyX, dirtyY, dirtyWidth, dirtyHeight);
+ r->context->buffer()->drawImage(image, dirtyX, dirtyY, dirtyWidth, dirtyHeight, dx, dy, dirtyWidth, dirtyHeight);
+ }
+ return args.This();
+}
+
+/*!
+ \qmlclass QtQuick2::CanvasGradient
+ \inqmlmodule QtQuick 2
+ \since QtQuick 2.0
+ \brief The Context2D opaque CanvasGradient interface.
+ */
+
+/*!
+ \qmlmethod QtQuick2::CanvasGradient QtQuick2::CanvasGradient::addColorStop(real offsetof, string color)
+ Adds a color stop with the given color to the gradient at the given offset.
+ 0.0 is the offset at one end of the gradient, 1.0 is the offset at the other end.
+
+ For example:
+ \code
+ var gradient = ctx.createLinearGradient(0, 0, 100, 100);
+ gradient.addColorStop(0.3, Qt.rgba(1, 0, 0, 1));
+ gradient.addColorStop(0.7, 'rgba(0, 255, 255, 1');
+ \endcode
+ */
+static v8::Handle<v8::Value> ctx2d_gradient_addColorStop(const v8::Arguments &args)
+{
+ QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(args.This());
+ if (!style)
+ V8THROW_ERROR("Not a CanvasGradient object");
+
+ QV8Engine *engine = V8ENGINE();
+
+ if (args.Length() == 2) {
+
+ if (!style->brush.gradient())
+ V8THROW_ERROR("Not a valid CanvasGradient object, can't get the gradient information");
+ QGradient gradient = *(style->brush.gradient());
+ qreal pos = args[0]->NumberValue();
+ QColor color;
+
+ if (args[1]->IsObject()) {
+ color = engine->toVariant(args[1], qMetaTypeId<QColor>()).value<QColor>();
+ } else {
+ color = qt_color_from_string(args[1]);
+ }
+ if (pos < 0.0 || pos > 1.0 || !qIsFinite(pos)) {
+ V8THROW_DOM(DOMEXCEPTION_INDEX_SIZE_ERR, "CanvasGradient: parameter offset out of range");
+ }
+
+ if (color.isValid()) {
+ gradient.setColorAt(pos, color);
+ } else {
+ V8THROW_DOM(DOMEXCEPTION_SYNTAX_ERR, "CanvasGradient: parameter color is not a valid color string");
+ }
+ style->brush = gradient;
+ }
+
+ return args.This();
+}
+
+
+void QQuickContext2D::beginPath()
+{
+ m_path = QPainterPath();
+ m_path.setFillRule(state.fillRule);
+}
+
+void QQuickContext2D::closePath()
+{
+ if (m_path.isEmpty())
+ return;
+
+ QRectF boundRect = m_path.boundingRect();
+ if (boundRect.width() || boundRect.height())
+ m_path.closeSubpath();
+ //FIXME:QPainterPath set the current point to (0,0) after close subpath
+ //should be the first point of the previous subpath
+}
+
+void QQuickContext2D::moveTo( qreal x, qreal y)
+{
+ //FIXME: moveTo should not close the previous subpath
+ m_path.moveTo(state.matrix.map(QPointF(x, y)));
+}
+
+void QQuickContext2D::lineTo( qreal x, qreal y)
+{
+ m_path.lineTo(state.matrix.map(QPointF(x, y)));
+}
+
+void QQuickContext2D::quadraticCurveTo(qreal cpx, qreal cpy,
+ qreal x, qreal y)
+{
+ m_path.quadTo(state.matrix.map(QPointF(cpx, cpy)),
+ state.matrix.map(QPointF(x, y)));
+}
+
+void QQuickContext2D::bezierCurveTo(qreal cp1x, qreal cp1y,
+ qreal cp2x, qreal cp2y,
+ qreal x, qreal y)
+{
+ m_path.cubicTo(state.matrix.map(QPointF(cp1x, cp1y)),
+ state.matrix.map(QPointF(cp2x, cp2y)),
+ state.matrix.map(QPointF(x, y)));
+}
+
+void QQuickContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radius)
+{
+ QPointF p0(m_path.currentPosition());
+
+ QPointF p1p0((p0.x() - p1.x()), (p0.y() - p1.y()));
+ QPointF p1p2((p2.x() - p1.x()), (p2.y() - p1.y()));
+ float p1p0_length = qSqrt(p1p0.x() * p1p0.x() + p1p0.y() * p1p0.y());
+ float p1p2_length = qSqrt(p1p2.x() * p1p2.x() + p1p2.y() * p1p2.y());
+
+ double cos_phi = (p1p0.x() * p1p2.x() + p1p0.y() * p1p2.y()) / (p1p0_length * p1p2_length);
+
+ // The points p0, p1, and p2 are on the same straight line (HTML5, 4.8.11.1.8)
+ // We could have used areCollinear() here, but since we're reusing
+ // the variables computed above later on we keep this logic.
+ if (qFuzzyCompare(qAbs(cos_phi), 1.0)) {
+ m_path.lineTo(p1);
+ return;
+ }
+
+ float tangent = radius / tan(acos(cos_phi) / 2);
+ float factor_p1p0 = tangent / p1p0_length;
+ QPointF t_p1p0((p1.x() + factor_p1p0 * p1p0.x()), (p1.y() + factor_p1p0 * p1p0.y()));
+
+ QPointF orth_p1p0(p1p0.y(), -p1p0.x());
+ float orth_p1p0_length = sqrt(orth_p1p0.x() * orth_p1p0.x() + orth_p1p0.y() * orth_p1p0.y());
+ float factor_ra = radius / orth_p1p0_length;
+
+ // angle between orth_p1p0 and p1p2 to get the right vector orthographic to p1p0
+ double cos_alpha = (orth_p1p0.x() * p1p2.x() + orth_p1p0.y() * p1p2.y()) / (orth_p1p0_length * p1p2_length);
+ if (cos_alpha < 0.f)
+ orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
+
+ QPointF p((t_p1p0.x() + factor_ra * orth_p1p0.x()), (t_p1p0.y() + factor_ra * orth_p1p0.y()));
+
+ // calculate angles for addArc
+ orth_p1p0 = QPointF(-orth_p1p0.x(), -orth_p1p0.y());
+ float sa = acos(orth_p1p0.x() / orth_p1p0_length);
+ if (orth_p1p0.y() < 0.f)
+ sa = 2 * Q_PI - sa;
+
+ // anticlockwise logic
+ bool anticlockwise = false;
+
+ float factor_p1p2 = tangent / p1p2_length;
+ QPointF t_p1p2((p1.x() + factor_p1p2 * p1p2.x()), (p1.y() + factor_p1p2 * p1p2.y()));
+ QPointF orth_p1p2((t_p1p2.x() - p.x()), (t_p1p2.y() - p.y()));
+ float orth_p1p2_length = sqrtf(orth_p1p2.x() * orth_p1p2.x() + orth_p1p2.y() * orth_p1p2.y());
+ float ea = acos(orth_p1p2.x() / orth_p1p2_length);
+ if (orth_p1p2.y() < 0)
+ ea = 2 * Q_PI - ea;
+ if ((sa > ea) && ((sa - ea) < Q_PI))
+ anticlockwise = true;
+ if ((sa < ea) && ((ea - sa) > Q_PI))
+ anticlockwise = true;
+
+ arc(p.x(), p.y(), radius, sa, ea, anticlockwise, false);
+}
+
+void QQuickContext2D::arcTo(qreal x1, qreal y1,
+ qreal x2, qreal y2,
+ qreal radius)
+{
+ QPointF st = state.matrix.map(QPointF(x1, y1));
+ QPointF end = state.matrix.map(QPointF(x2, y2));
+
+ if (!m_path.elementCount()) {
+ m_path.moveTo(st);
+ } else if (st == m_path.currentPosition() || st == end || !radius) {
+ m_path.lineTo(st);
+ } else {
+ addArcTo(st, end, radius);
+ }
+}
+
+void QQuickContext2D::rect(qreal x, qreal y,
+ qreal w, qreal h)
+{
+ m_path.addPolygon(state.matrix.map(QRectF(x, y, w, h)));
+}
+
+void QQuickContext2D::roundedRect(qreal x, qreal y,
+ qreal w, qreal h,
+ qreal xr, qreal yr)
+{
+ QPainterPath path;
+ path.addRoundedRect(QRectF(x, y, w, h), xr, yr, Qt::AbsoluteSize);
+ m_path.addPath(state.matrix.map(path));
+}
+
+void QQuickContext2D::ellipse(qreal x, qreal y,
+ qreal w, qreal h)
+{
+ QPainterPath path;
+ path.addEllipse(x, y, w, h);
+ m_path.addPath(state.matrix.map(path));
+}
+
+void QQuickContext2D::text(const QString& str, qreal x, qreal y)
+{
+ QPainterPath path;
+ path.addText(x, y, state.font, str);
+ m_path.addPath(state.matrix.map(path));
+}
+
+void QQuickContext2D::arc(qreal xc,
+ qreal yc,
+ qreal radius,
+ qreal sar,
+ qreal ear,
+ bool antiClockWise,
+ bool transform)
+{
+
+ if (transform) {
+ QPointF point = state.matrix.map(QPointF(xc, yc));
+ xc = point.x();
+ yc = point.y();
+ }
+ //### HACK
+
+ // In Qt we don't switch the coordinate system for degrees
+ // and still use the 0,0 as bottom left for degrees so we need
+ // to switch
+ sar = -sar;
+ ear = -ear;
+ antiClockWise = !antiClockWise;
+ //end hack
+
+ float sa = DEGREES(sar);
+ float ea = DEGREES(ear);
+
+ double span = 0;
+
+ double xs = xc - radius;
+ double ys = yc - radius;
+ double width = radius*2;
+ double height = radius*2;
+ if ((!antiClockWise && (ea - sa >= 360)) || (antiClockWise && (sa - ea >= 360)))
+ // If the anticlockwise argument is false and endAngle-startAngle is equal to or greater than 2*PI, or, if the
+ // anticlockwise argument is true and startAngle-endAngle is equal to or greater than 2*PI, then the arc is the whole
+ // circumference of this circle.
+ span = 360;
+ else {
+ if (!antiClockWise && (ea < sa)) {
+ span += 360;
+ } else if (antiClockWise && (sa < ea)) {
+ span -= 360;
+ }
+ //### this is also due to switched coordinate system
+ // we would end up with a 0 span instead of 360
+ if (!(qFuzzyCompare(span + (ea - sa) + 1, 1) &&
+ qFuzzyCompare(qAbs(span), 360))) {
+ span += ea - sa;
+ }
+ if (!m_path.elementCount())
+ m_path.moveTo(xs, ys);
+ }
+
+
+ if (transform) {
+ QPointF currentPos = m_path.currentPosition();
+ QPointF startPos = QPointF(xc + radius * qCos(sar),
+ yc - radius * qSin(sar));
+ if (currentPos != startPos)
+ m_path.lineTo(startPos);
+ }
+
+ m_path.arcTo(xs, ys, width, height, sa, span);
+}
+
+int baseLineOffset(QQuickContext2D::TextBaseLineType value, const QFontMetrics &metrics)
+{
+ int offset = 0;
+ switch (value) {
+ case QQuickContext2D::Top:
+ break;
+ case QQuickContext2D::Alphabetic:
+ case QQuickContext2D::Middle:
+ case QQuickContext2D::Hanging:
+ offset = metrics.ascent();
+ break;
+ case QQuickContext2D::Bottom:
+ offset = metrics.height();
+ break;
+ }
+ return offset;
+}
+
+static int textAlignOffset(QQuickContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &text)
+{
+ int offset = 0;
+ if (value == QQuickContext2D::Start)
+ value = QGuiApplication::layoutDirection() == Qt::LeftToRight ? QQuickContext2D::Left : QQuickContext2D::Right;
+ else if (value == QQuickContext2D::End)
+ value = QGuiApplication::layoutDirection() == Qt::LeftToRight ? QQuickContext2D::Right: QQuickContext2D::Left;
+ switch (value) {
+ case QQuickContext2D::Center:
+ offset = metrics.width(text)/2;
+ break;
+ case QQuickContext2D::Right:
+ offset = metrics.width(text);
+ case QQuickContext2D::Left:
+ default:
+ break;
+ }
+ return offset;
+}
+
+
+QImage QQuickContext2D::createImage(const QUrl& url)
+{
+ return m_canvas->loadedImage(url);
+}
+
+QPainterPath QQuickContext2D::createTextGlyphs(qreal x, qreal y, const QString& text)
+{
+ const QFontMetrics metrics(state.font);
+ int yoffset = baseLineOffset(static_cast<QQuickContext2D::TextBaseLineType>(state.textBaseline), metrics);
+ int xoffset = textAlignOffset(static_cast<QQuickContext2D::TextAlignType>(state.textAlign), metrics, text);
+
+ QPainterPath textPath;
+
+ textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), state.font, text);
+ textPath = state.matrix.map(textPath);
+ return textPath;
+}
+
+
+bool QQuickContext2D::isPointInPath(qreal x, qreal y) const
+{
+ return m_path.contains(QPointF(x, y));
+}
+
+QQuickContext2D::QQuickContext2D(QQuickCanvasItem* item)
+ : m_canvas(item)
+ , m_buffer(new QQuickContext2DCommandBuffer)
+ , m_v8engine(0)
+{
+ reset();
+}
+
+QQuickContext2D::~QQuickContext2D()
+{
+ delete m_buffer;
+}
+
+v8::Handle<v8::Object> QQuickContext2D::v8value() const
+{
+ return m_v8value;
+}
+
+QQuickContext2DEngineData::QQuickContext2DEngineData(QV8Engine *engine)
+{
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(engine->context());
+
+ v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
+ ft->InstanceTemplate()->SetHasExternalResource(true);
+ ft->PrototypeTemplate()->SetAccessor(v8::String::New("canvas"), ctx2d_canvas, 0, v8::External::Wrap(engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("restore"), V8FUNCTION(ctx2d_restore, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("reset"), V8FUNCTION(ctx2d_reset, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("save"), V8FUNCTION(ctx2d_save, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("rotate"), V8FUNCTION(ctx2d_rotate, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("scale"), V8FUNCTION(ctx2d_scale, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("resetTransform"), V8FUNCTION(ctx2d_resetTransform, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("setTransform"), V8FUNCTION(ctx2d_setTransform, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("transform"), V8FUNCTION(ctx2d_transform, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("translate"), V8FUNCTION(ctx2d_translate, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("shear"), V8FUNCTION(ctx2d_shear, engine));
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("globalAlpha"), ctx2d_globalAlpha, ctx2d_globalAlpha_set, v8::External::Wrap(engine));
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("globalCompositeOperation"), ctx2d_globalCompositeOperation, ctx2d_globalCompositeOperation_set, v8::External::Wrap(engine));
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("fillRule"), ctx2d_fillRule, ctx2d_fillRule_set, v8::External::Wrap(engine));
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("fillStyle"), ctx2d_fillStyle, ctx2d_fillStyle_set, v8::External::Wrap(engine));
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("strokeStyle"), ctx2d_strokeStyle, ctx2d_strokeStyle_set, v8::External::Wrap(engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("createLinearGradient"), V8FUNCTION(ctx2d_createLinearGradient, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("createRadialGradient"), V8FUNCTION(ctx2d_createRadialGradient, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("createConicalGradient"), V8FUNCTION(ctx2d_createConicalGradient, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("createPattern"), V8FUNCTION(ctx2d_createPattern, engine));
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("lineCap"), ctx2d_lineCap, ctx2d_lineCap_set, v8::External::Wrap(engine));
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("lineJoin"), ctx2d_lineJoin, ctx2d_lineJoin_set, v8::External::Wrap(engine));
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("lineWidth"), ctx2d_lineWidth, ctx2d_lineWidth_set, v8::External::Wrap(engine));
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("miterLimit"), ctx2d_miterLimit, ctx2d_miterLimit_set, v8::External::Wrap(engine));
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowBlur"), ctx2d_shadowBlur, ctx2d_shadowBlur_set, v8::External::Wrap(engine));
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowColor"), ctx2d_shadowColor, ctx2d_shadowColor_set, v8::External::Wrap(engine));
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetX"), ctx2d_shadowOffsetX, ctx2d_shadowOffsetX_set, v8::External::Wrap(engine));
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("shadowOffsetY"), ctx2d_shadowOffsetY, ctx2d_shadowOffsetY_set, v8::External::Wrap(engine));
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("path"), ctx2d_path, ctx2d_path_set, v8::External::Wrap(engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("clearRect"), V8FUNCTION(ctx2d_clearRect, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("fillRect"), V8FUNCTION(ctx2d_fillRect, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("strokeRect"), V8FUNCTION(ctx2d_strokeRect, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("arc"), V8FUNCTION(ctx2d_arc, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("arcTo"), V8FUNCTION(ctx2d_arcTo, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("beginPath"), V8FUNCTION(ctx2d_beginPath, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("bezierCurveTo"), V8FUNCTION(ctx2d_bezierCurveTo, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("clip"), V8FUNCTION(ctx2d_clip, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("closePath"), V8FUNCTION(ctx2d_closePath, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("fill"), V8FUNCTION(ctx2d_fill, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("lineTo"), V8FUNCTION(ctx2d_lineTo, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("moveTo"), V8FUNCTION(ctx2d_moveTo, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("quadraticCurveTo"), V8FUNCTION(ctx2d_quadraticCurveTo, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("rect"), V8FUNCTION(ctx2d_rect, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("roundedRect"), V8FUNCTION(ctx2d_roundedRect, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("text"), V8FUNCTION(ctx2d_text, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("ellipse"), V8FUNCTION(ctx2d_ellipse, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("stroke"), V8FUNCTION(ctx2d_stroke, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("isPointInPath"), V8FUNCTION(ctx2d_isPointInPath, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("drawFocusRing"), V8FUNCTION(ctx2d_drawFocusRing, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("caretBlinkRate"), V8FUNCTION(ctx2d_caretBlinkRate, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("setCaretSelectionRect"), V8FUNCTION(ctx2d_setCaretSelectionRect, engine));
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("font"), ctx2d_font, ctx2d_font_set, v8::External::Wrap(engine));
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("textAlign"), ctx2d_textAlign, ctx2d_textAlign_set, v8::External::Wrap(engine));
+ ft->InstanceTemplate()->SetAccessor(v8::String::New("textBaseline"), ctx2d_textBaseline, ctx2d_textBaseline_set, v8::External::Wrap(engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("fillText"), V8FUNCTION(ctx2d_fillText, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("measureText"), V8FUNCTION(ctx2d_measureText, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("strokeText"), V8FUNCTION(ctx2d_strokeText, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("drawImage"), V8FUNCTION(ctx2d_drawImage, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("createImageData"), V8FUNCTION(ctx2d_createImageData, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("getImageData"), V8FUNCTION(ctx2d_getImageData, engine));
+ ft->PrototypeTemplate()->Set(v8::String::New("putImageData"), V8FUNCTION(ctx2d_putImageData, engine));
+
+ constructorContext = qPersistentNew(ft->GetFunction());
+
+ v8::Local<v8::FunctionTemplate> ftGradient = v8::FunctionTemplate::New();
+ ftGradient->InstanceTemplate()->SetHasExternalResource(true);
+ ftGradient->PrototypeTemplate()->Set(v8::String::New("addColorStop"), V8FUNCTION(ctx2d_gradient_addColorStop, engine));
+ constructorGradient = qPersistentNew(ftGradient->GetFunction());
+
+ v8::Local<v8::FunctionTemplate> ftPattern = v8::FunctionTemplate::New();
+ ftPattern->InstanceTemplate()->SetHasExternalResource(true);
+ constructorPattern = qPersistentNew(ftPattern->GetFunction());
+
+ v8::Local<v8::FunctionTemplate> ftPixelArray = v8::FunctionTemplate::New();
+ ftPixelArray->InstanceTemplate()->SetHasExternalResource(true);
+ ftPixelArray->InstanceTemplate()->SetAccessor(v8::String::New("length"), ctx2d_pixelArray_length, 0, v8::External::Wrap(engine));
+ ftPixelArray->InstanceTemplate()->SetIndexedPropertyHandler(ctx2d_pixelArray_indexed, ctx2d_pixelArray_indexed_set, 0, 0, 0, v8::External::Wrap(engine));
+ constructorPixelArray = qPersistentNew(ftPixelArray->GetFunction());
+
+ v8::Local<v8::FunctionTemplate> ftImageData = v8::FunctionTemplate::New();
+ ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("width"), ctx2d_imageData_width, 0, v8::External::Wrap(engine));
+ ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("height"), ctx2d_imageData_height, 0, v8::External::Wrap(engine));
+ ftImageData->InstanceTemplate()->SetAccessor(v8::String::New("data"), ctx2d_imageData_data, 0, v8::External::Wrap(engine));
+ ftImageData->PrototypeTemplate()->Set(v8::String::New("mirror"), V8FUNCTION(ctx2d_imageData_mirror, engine));
+ ftImageData->PrototypeTemplate()->Set(v8::String::New("filter"), V8FUNCTION(ctx2d_imageData_filter, engine));
+ ftImageData->InstanceTemplate()->SetInternalFieldCount(1);
+ constructorImageData = qPersistentNew(ftImageData->GetFunction());
+}
+
+QQuickContext2DEngineData::~QQuickContext2DEngineData()
+{
+ qPersistentDispose(constructorContext);
+ qPersistentDispose(constructorGradient);
+ qPersistentDispose(constructorPattern);
+ qPersistentDispose(constructorImageData);
+ qPersistentDispose(constructorPixelArray);
+}
+
+void QQuickContext2D::popState()
+{
+ if (m_stateStack.isEmpty())
+ return;
+
+ QQuickContext2D::State newState = m_stateStack.pop();
+
+ if (state.matrix != newState.matrix)
+ buffer()->updateMatrix(newState.matrix);
+
+ if (newState.globalAlpha != state.globalAlpha)
+ buffer()->setGlobalAlpha(newState.globalAlpha);
+
+ if (newState.globalCompositeOperation != state.globalCompositeOperation)
+ buffer()->setGlobalCompositeOperation(newState.globalCompositeOperation);
+
+ if (newState.fillStyle != state.fillStyle)
+ buffer()->setFillStyle(newState.fillStyle);
+
+ if (newState.strokeStyle != state.strokeStyle)
+ buffer()->setStrokeStyle(newState.strokeStyle);
+
+ if (newState.lineWidth != state.lineWidth)
+ buffer()->setLineWidth(newState.lineWidth);
+
+ if (newState.lineCap != state.lineCap)
+ buffer()->setLineCap(newState.lineCap);
+
+ if (newState.lineJoin != state.lineJoin)
+ buffer()->setLineJoin(newState.lineJoin);
+
+ if (newState.miterLimit != state.miterLimit)
+ buffer()->setMiterLimit(newState.miterLimit);
+
+ if (newState.clipPath != state.clipPath) {
+ buffer()->clip(newState.clipPath);
+ }
+
+ if (newState.shadowBlur != state.shadowBlur)
+ buffer()->setShadowBlur(newState.shadowBlur);
+
+ if (newState.shadowColor != state.shadowColor)
+ buffer()->setShadowColor(newState.shadowColor);
+
+ if (newState.shadowOffsetX != state.shadowOffsetX)
+ buffer()->setShadowOffsetX(newState.shadowOffsetX);
+
+ if (newState.shadowOffsetY != state.shadowOffsetY)
+ buffer()->setShadowOffsetY(newState.shadowOffsetY);
+ state = newState;
+}
+void QQuickContext2D::pushState()
+{
+ m_stateStack.push(state);
+}
+
+void QQuickContext2D::reset()
+{
+ QQuickContext2D::State newState;
+ newState.matrix = QTransform();
+
+ QPainterPath defaultClipPath;
+
+ QRect r(0, 0, m_canvas->canvasSize().width(), m_canvas->canvasSize().height());
+ r = r.united(m_canvas->canvasWindow().toRect());
+ defaultClipPath.addRect(r);
+ newState.clipPath = defaultClipPath;
+ newState.clipPath.setFillRule(Qt::WindingFill);
+
+ newState.strokeStyle = QColor("#000000");
+ newState.fillStyle = QColor("#000000");
+ newState.fillPatternRepeatX = false;
+ newState.fillPatternRepeatY = false;
+ newState.strokePatternRepeatX = false;
+ newState.strokePatternRepeatY = false;
+ newState.fillRule = Qt::WindingFill;
+ newState.globalAlpha = 1.0;
+ newState.lineWidth = 1;
+ newState.lineCap = Qt::FlatCap;
+ newState.lineJoin = Qt::MiterJoin;
+ newState.miterLimit = 10;
+ newState.shadowOffsetX = 0;
+ newState.shadowOffsetY = 0;
+ newState.shadowBlur = 0;
+ newState.shadowColor = qRgba(0, 0, 0, 0);
+ newState.globalCompositeOperation = QPainter::CompositionMode_SourceOver;
+ newState.font = QFont(QLatin1String("sans-serif"), 10);
+ newState.textAlign = QQuickContext2D::Start;
+ newState.textBaseline = QQuickContext2D::Alphabetic;
+
+ m_stateStack.clear();
+ m_stateStack.push(newState);
+ popState();
+ m_buffer->clearRect(0, 0, m_canvas->width(), m_canvas->height());
+}
+
+void QQuickContext2D::setV8Engine(QV8Engine *engine)
+{
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(engine->context());
+
+ if (m_v8engine != engine) {
+ m_v8engine = engine;
+
+ qPersistentDispose(m_v8value);
+
+ if (m_v8engine == 0)
+ return;
+
+ QQuickContext2DEngineData *ed = engineData(engine);
+ m_v8value = qPersistentNew(ed->constructorContext->NewInstance());
+ QV8Context2DResource *r = new QV8Context2DResource(engine);
+ r->context = this;
+ m_v8value->SetExternalResource(r);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/quick/items/context2d/qquickcontext2d_p.h b/src/quick/items/context2d/qquickcontext2d_p.h
new file mode 100644
index 0000000000..0f320d9a7f
--- /dev/null
+++ b/src/quick/items/context2d/qquickcontext2d_p.h
@@ -0,0 +1,210 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKCONTEXT2D_P_H
+#define QQUICKCONTEXT2D_P_H
+
+#include <QtQuick/qtquickglobal.h>
+#include <QtDeclarative/qdeclarative.h>
+#include <QtDeclarative/qdeclarativecomponent.h>
+
+#include <QtGui/qpainter.h>
+#include <QtGui/qpainterpath.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstack.h>
+#include <private/qv8engine_p.h>
+
+
+
+#define QQUICKCONTEXT2D_DEBUG //enable this for just DEBUG purpose!
+
+#ifdef QQUICKCONTEXT2D_DEBUG
+#include <QElapsedTimer>
+#endif
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickCanvasItem;
+class QQuickContext2DCommandBuffer;
+class QDeclarativePixmap;
+
+class Q_QUICK_EXPORT QQuickContext2D
+{
+public:
+ enum TextBaseLineType { Alphabetic=0, Top, Middle, Bottom, Hanging};
+ enum TextAlignType { Start=0, End, Left, Right, Center};
+ enum PaintCommand {
+ Invalid = 0,
+ UpdateMatrix,
+ ClearRect,
+ FillRect,
+ StrokeRect,
+ Fill,
+ Stroke,
+ Clip,
+ UpdateBrush,
+ GlobalAlpha,
+ GlobalCompositeOperation,
+ StrokeStyle,
+ FillStyle,
+ LineWidth,
+ LineCap,
+ LineJoin,
+ MiterLimit,
+ ShadowOffsetX,
+ ShadowOffsetY,
+ ShadowBlur,
+ ShadowColor,
+ Font,
+ TextBaseline,
+ TextAlign,
+ FillText,
+ StrokeText,
+ DrawImage,
+ GetImageData
+ };
+
+
+ struct State {
+ State()
+ : strokeStyle(QColor("#000000"))
+ , fillStyle(QColor("#000000"))
+ , fillPatternRepeatX(false)
+ , fillPatternRepeatY(false)
+ , strokePatternRepeatX(false)
+ , strokePatternRepeatY(false)
+ , fillRule(Qt::WindingFill)
+ , globalAlpha(1.0)
+ , lineWidth(1)
+ , lineCap(Qt::FlatCap)
+ , lineJoin(Qt::MiterJoin)
+ , miterLimit(10)
+ , shadowOffsetX(0)
+ , shadowOffsetY(0)
+ , shadowBlur(0)
+ , shadowColor(qRgba(0, 0, 0, 0))
+ , globalCompositeOperation(QPainter::CompositionMode_SourceOver)
+ , font(QFont(QLatin1String("sans-serif"), 10))
+ , textAlign(QQuickContext2D::Start)
+ , textBaseline(QQuickContext2D::Alphabetic)
+ {
+ }
+
+ QTransform matrix;
+ QPainterPath clipPath;
+ QBrush strokeStyle;
+ QBrush fillStyle;
+ bool fillPatternRepeatX:1;
+ bool fillPatternRepeatY:1;
+ bool strokePatternRepeatX:1;
+ bool strokePatternRepeatY:1;
+ Qt::FillRule fillRule;
+ qreal globalAlpha;
+ qreal lineWidth;
+ Qt::PenCapStyle lineCap;
+ Qt::PenJoinStyle lineJoin;
+ qreal miterLimit;
+ qreal shadowOffsetX;
+ qreal shadowOffsetY;
+ qreal shadowBlur;
+ QColor shadowColor;
+ QPainter::CompositionMode globalCompositeOperation;
+ QFont font;
+ QQuickContext2D::TextAlignType textAlign;
+ QQuickContext2D::TextBaseLineType textBaseline;
+ };
+
+ QQuickContext2D(QQuickCanvasItem* item);
+ ~QQuickContext2D();
+
+ inline QQuickCanvasItem* canvas() const {return m_canvas;}
+ inline QQuickContext2DCommandBuffer* buffer() const {return m_buffer;}
+
+ v8::Handle<v8::Object> v8value() const;
+ void setV8Engine(QV8Engine *eng);
+ void popState();
+ void pushState();
+ void reset();
+
+ // path API
+ void beginPath();
+ void closePath();
+ void moveTo(qreal x, qreal y);
+ void lineTo(qreal x, qreal y);
+ void quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y);
+ void bezierCurveTo(qreal cp1x, qreal cp1y,
+ qreal cp2x, qreal cp2y, qreal x, qreal y);
+ void arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius);
+ void rect(qreal x, qreal y, qreal w, qreal h);
+ void roundedRect(qreal x, qreal y,qreal w, qreal h, qreal xr, qreal yr);
+ void ellipse(qreal x, qreal y,qreal w, qreal h);
+ void text(const QString& str, qreal x, qreal y);
+ void arc(qreal x, qreal y, qreal radius,
+ qreal startAngle, qreal endAngle,
+ bool anticlockwise, bool transform=true);
+ void addArcTo(const QPointF& p1, const QPointF& p2, float radius);
+
+ bool isPointInPath(qreal x, qreal y) const;
+
+ QPainterPath createTextGlyphs(qreal x, qreal y, const QString& text);
+ QImage createImage(const QUrl& url);
+
+ State state;
+ QStack<QQuickContext2D::State> m_stateStack;
+ QQuickCanvasItem* m_canvas;
+ QQuickContext2DCommandBuffer* m_buffer;
+ QPainterPath m_path;
+ v8::Local<v8::Value> m_fillStyle;
+ v8::Local<v8::Value> m_strokeStyle;
+ v8::Handle<v8::Value> m_v8path;
+ QV8Engine *m_v8engine;
+ v8::Persistent<v8::Object> m_v8value;
+};
+
+
+QT_END_NAMESPACE
+QML_DECLARE_TYPE(QQuickContext2D)
+
+QT_END_HEADER
+
+#endif // QQUICKCONTEXT2D_P_H
diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
new file mode 100644
index 0000000000..476e7e2cf8
--- /dev/null
+++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer.cpp
@@ -0,0 +1,469 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickcontext2dcommandbuffer_p.h"
+#include "qquickcanvasitem_p.h"
+#include <qdeclarative.h>
+#include <QtCore/QMutex>
+
+#define HAS_SHADOW(offsetX, offsetY, blur, color) (color.isValid() && color.alpha() && (blur || offsetX || offsetY))
+
+QT_BEGIN_NAMESPACE
+
+void qt_image_boxblur(QImage& image, int radius, bool quality);
+
+static QImage makeShadowImage(const QImage& image, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
+{
+ QImage shadowImg(image.width() + blur + qAbs(offsetX),
+ image.height() + blur + qAbs(offsetY),
+ QImage::Format_ARGB32_Premultiplied);
+ shadowImg.fill(0);
+ QPainter tmpPainter(&shadowImg);
+ tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
+ qreal shadowX = offsetX > 0? offsetX : 0;
+ qreal shadowY = offsetY > 0? offsetY : 0;
+
+ tmpPainter.drawImage(shadowX, shadowY, image);
+ tmpPainter.end();
+
+ if (blur > 0)
+ qt_image_boxblur(shadowImg, blur/2, true);
+
+ // blacken the image with shadow color...
+ tmpPainter.begin(&shadowImg);
+ tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
+ tmpPainter.fillRect(shadowImg.rect(), color);
+ tmpPainter.end();
+ return shadowImg;
+}
+
+static void fillRectShadow(QPainter* p, QRectF shadowRect, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
+{
+ QRectF r = shadowRect;
+ r.moveTo(0, 0);
+
+ QImage shadowImage(r.size().width() + 1, r.size().height() + 1, QImage::Format_ARGB32_Premultiplied);
+ QPainter tp;
+ tp.begin(&shadowImage);
+ tp.fillRect(r, p->brush());
+ tp.end();
+ shadowImage = makeShadowImage(shadowImage, offsetX, offsetY, blur, color);
+
+ qreal dx = shadowRect.left() + (offsetX < 0? offsetX:0);
+ qreal dy = shadowRect.top() + (offsetY < 0? offsetY:0);
+
+ p->drawImage(dx, dy, shadowImage);
+ p->fillRect(shadowRect, p->brush());
+}
+
+static void fillShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
+{
+ QRectF r = path.boundingRect();
+ QImage img(r.size().width() + r.left() + 1,
+ r.size().height() + r.top() + 1,
+ QImage::Format_ARGB32_Premultiplied);
+ img.fill(0);
+ QPainter tp(&img);
+ tp.fillPath(path.translated(0, 0), p->brush());
+ tp.end();
+
+ QImage shadowImage = makeShadowImage(img, offsetX, offsetY, blur, color);
+ qreal dx = r.left() + (offsetX < 0? offsetX:0);
+ qreal dy = r.top() + (offsetY < 0? offsetY:0);
+
+ p->drawImage(dx, dy, shadowImage);
+ p->fillPath(path, p->brush());
+}
+
+static void strokeShadowPath(QPainter* p, const QPainterPath& path, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
+{
+ QRectF r = path.boundingRect();
+ QImage img(r.size().width() + r.left() + 1,
+ r.size().height() + r.top() + 1,
+ QImage::Format_ARGB32_Premultiplied);
+ img.fill(0);
+ QPainter tp(&img);
+ tp.strokePath(path, p->pen());
+ tp.end();
+
+ QImage shadowImage = makeShadowImage(img, offsetX, offsetY, blur, color);
+ qreal dx = r.left() + (offsetX < 0? offsetX:0);
+ qreal dy = r.top() + (offsetY < 0? offsetY:0);
+ p->drawImage(dx, dy, shadowImage);
+ p->strokePath(path, p->pen());
+}
+static inline void drawRepeatPattern(QPainter* p, const QImage& image, const QRectF& rect, const bool repeatX, const bool repeatY)
+{
+ // Patterns must be painted so that the top left of the first image is anchored at
+ // the origin of the coordinate space
+ if (!image.isNull()) {
+ int w = image.width();
+ int h = image.height();
+ int startX, startY;
+ QRect r(static_cast<int>(rect.x()), static_cast<int>(rect.y()), static_cast<int>(rect.width()), static_cast<int>(rect.height()));
+
+ // startX, startY is the coordinate of the first image we need to put on the left-top of the rect
+ if (repeatX && repeatY) {
+ // repeat
+ // startX, startY is at the left top side of the left-top of the rect
+ startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w);
+ startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h);
+ } else {
+ if (!repeatX && !repeatY) {
+ // no-repeat
+ // only draw the image once at orgin once, check if need to draw
+ QRect imageRect(0, 0, w, h);
+ if (imageRect.intersects(r)) {
+ startX = 0;
+ startY = 0;
+ } else
+ return;
+ } else if (repeatX && !repeatY) {
+ // repeat-x
+ // startY is fixed, but startX change based on the left-top of the rect
+ QRect imageRect(r.x(), 0, r.width(), h);
+ if (imageRect.intersects(r)) {
+ startX = r.x() >=0 ? r.x() - (r.x() % w) : r.x() - (w - qAbs(r.x()) % w);
+ startY = 0;
+ } else
+ return;
+ } else {
+ // repeat-y
+ // startX is fixed, but startY change based on the left-top of the rect
+ QRect imageRect(0, r.y(), w, r.height());
+ if (imageRect.intersects(r)) {
+ startX = 0;
+ startY = r.y() >=0 ? r.y() - (r.y() % h) : r.y() - (h - qAbs(r.y()) % h);
+ } else
+ return;
+ }
+ }
+
+ int x = startX;
+ int y = startY;
+ do {
+ // repeat Y
+ do {
+ // repeat X
+ QRect imageRect(x, y, w, h);
+ QRect intersectRect = imageRect.intersected(r);
+ QPoint destStart(intersectRect.x(), intersectRect.y());
+ QRect sourceRect(intersectRect.x() - imageRect.x(), intersectRect.y() - imageRect.y(), intersectRect.width(), intersectRect.height());
+
+ p->drawImage(destStart, image, sourceRect);
+ x += w;
+ } while (repeatX && x < r.x() + r.width());
+ x = startX;
+ y += h;
+ } while (repeatY && y < r.y() + r.height());
+ }
+}
+
+QPen QQuickContext2DCommandBuffer::makePen(const QQuickContext2D::State& state)
+{
+ QPen pen;
+ pen.setWidthF(state.lineWidth);
+ pen.setCapStyle(state.lineCap);
+ pen.setJoinStyle(state.lineJoin);
+ pen.setMiterLimit(state.miterLimit);
+ pen.setBrush(state.strokeStyle);
+ return pen;
+}
+
+void QQuickContext2DCommandBuffer::setPainterState(QPainter* p, const QQuickContext2D::State& state, const QPen& pen)
+{
+ p->setTransform(state.matrix * p->transform());
+
+ if (pen != p->pen())
+ p->setPen(pen);
+
+ if (state.fillStyle != p->brush())
+ p->setBrush(state.fillStyle);
+
+ if (state.font != p->font())
+ p->setFont(state.font);
+
+ if (state.globalAlpha != p->opacity()) {
+ p->setOpacity(state.globalAlpha);
+ }
+
+ if (state.globalCompositeOperation != p->compositionMode())
+ p->setCompositionMode(state.globalCompositeOperation);
+}
+
+void QQuickContext2DCommandBuffer::replay(QPainter* p, QQuickContext2D::State& state)
+{
+ if (!p)
+ return;
+
+ reset();
+
+ QTransform originMatrix = p->transform();
+
+ QPen pen = makePen(state);
+ setPainterState(p, state, pen);
+
+ while (hasNext()) {
+ QQuickContext2D::PaintCommand cmd = takeNextCommand();
+ switch (cmd) {
+ case QQuickContext2D::UpdateMatrix:
+ {
+ state.matrix = takeMatrix();
+ p->setTransform(state.matrix * originMatrix);
+ break;
+ }
+ case QQuickContext2D::ClearRect:
+ {
+ QPainter::CompositionMode cm = p->compositionMode();
+ qreal alpha = p->opacity();
+ p->setCompositionMode(QPainter::CompositionMode_Source);
+ p->setOpacity(0);
+ p->fillRect(takeRect(), QColor(qRgba(0, 0, 0, 0)));
+ p->setCompositionMode(cm);
+ p->setOpacity(alpha);
+ break;
+ }
+ case QQuickContext2D::FillRect:
+ {
+ QRectF r = takeRect();
+ if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor))
+ fillRectShadow(p, r, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
+ else
+ p->fillRect(r, p->brush());
+ break;
+ }
+ case QQuickContext2D::ShadowColor:
+ {
+ state.shadowColor = takeColor();
+ break;
+ }
+ case QQuickContext2D::ShadowBlur:
+ {
+ state.shadowBlur = takeShadowBlur();
+ break;
+ }
+ case QQuickContext2D::ShadowOffsetX:
+ {
+ state.shadowOffsetX = takeShadowOffsetX();
+ break;
+ }
+ case QQuickContext2D::ShadowOffsetY:
+ {
+ state.shadowOffsetY = takeShadowOffsetY();
+ break;
+ }
+ case QQuickContext2D::FillStyle:
+ {
+ state.fillStyle = takeFillStyle();
+ state.fillPatternRepeatX = takeBool();
+ state.fillPatternRepeatY = takeBool();
+ p->setBrush(state.fillStyle);
+ break;
+ }
+ case QQuickContext2D::StrokeStyle:
+ {
+ state.strokeStyle = takeStrokeStyle();
+ state.strokePatternRepeatX = takeBool();
+ state.strokePatternRepeatY = takeBool();
+ pen.setBrush(state.strokeStyle);
+ p->setPen(pen);
+ break;
+ }
+ case QQuickContext2D::LineWidth:
+ {
+ state.lineWidth = takeLineWidth();
+ pen.setWidth(state.lineWidth);
+ p->setPen(pen);
+ break;
+ }
+ case QQuickContext2D::LineCap:
+ {
+ state.lineCap = takeLineCap();
+ pen.setCapStyle(state.lineCap);
+ p->setPen(pen);
+ break;
+ }
+ case QQuickContext2D::LineJoin:
+ {
+ state.lineJoin = takeLineJoin();
+ pen.setJoinStyle(state.lineJoin);
+ p->setPen(pen);
+ break;
+ }
+ case QQuickContext2D::MiterLimit:
+ {
+ state.miterLimit = takeMiterLimit();
+ pen.setMiterLimit(state.miterLimit);
+ p->setPen(pen);
+ break;
+ }
+ case QQuickContext2D::TextAlign:
+ case QQuickContext2D::TextBaseline:
+ break;
+ case QQuickContext2D::Fill:
+ {
+ QPainterPath path = takePath();
+ path.closeSubpath();
+ if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor))
+ fillShadowPath(p,path, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
+ else
+ p->fillPath(path, p->brush());
+ break;
+ }
+ case QQuickContext2D::Stroke:
+ {
+ if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor))
+ strokeShadowPath(p,takePath(), state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
+ else
+ p->strokePath(takePath(), p->pen());
+ break;
+ }
+ case QQuickContext2D::Clip:
+ {
+ state.clipPath = takePath();
+ p->setClipping(true);
+ p->setClipPath(state.clipPath);
+ break;
+ }
+ case QQuickContext2D::GlobalAlpha:
+ {
+ state.globalAlpha = takeGlobalAlpha();
+ p->setOpacity(state.globalAlpha);
+ break;
+ }
+ case QQuickContext2D::GlobalCompositeOperation:
+ {
+ state.globalCompositeOperation = takeGlobalCompositeOperation();
+ p->setCompositionMode(state.globalCompositeOperation);
+ break;
+ }
+ case QQuickContext2D::DrawImage:
+ {
+ qreal sx = takeReal();
+ qreal sy = takeReal();
+ qreal sw = takeReal();
+ qreal sh = takeReal();
+ qreal dx = takeReal();
+ qreal dy = takeReal();
+ qreal dw = takeReal();
+ qreal dh = takeReal();
+ QImage image = takeImage();
+
+ if (!image.isNull()) {
+ if (sw == -1 || sh == -1) {
+ sw = image.width();
+ sh = image.height();
+ }
+ if (sx != 0 || sy != 0 || sw != image.width() || sh != image.height())
+ image = image.copy(sx, sy, sw, sh);
+
+ image = image.scaled(dw, dh);
+
+ if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor)) {
+ QImage shadow = makeShadowImage(image, state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
+ qreal shadow_dx = dx + (state.shadowOffsetX < 0? state.shadowOffsetY:0);
+ qreal shadow_dy = dy + (state.shadowOffsetX < 0? state.shadowOffsetY:0);
+ p->drawImage(shadow_dx, shadow_dy, shadow);
+ }
+ p->drawImage(dx, dy, image);
+ }
+ break;
+ }
+ case QQuickContext2D::GetImageData:
+ {
+ //TODO:
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ p->end();
+}
+
+QQuickContext2DCommandBuffer::QQuickContext2DCommandBuffer()
+ : cmdIdx(0)
+ , intIdx(0)
+ , boolIdx(0)
+ , realIdx(0)
+ , colorIdx(0)
+ , matrixIdx(0)
+ , brushIdx(0)
+ , pathIdx(0)
+ , imageIdx(0)
+{
+}
+
+
+QQuickContext2DCommandBuffer::~QQuickContext2DCommandBuffer()
+{
+}
+
+void QQuickContext2DCommandBuffer::clear()
+{
+ commands.clear();
+ ints.clear();
+ bools.clear();
+ reals.clear();
+ colors.clear();
+ matrixes.clear();
+ brushes.clear();
+ pathes.clear();
+ images.clear();
+ reset();
+}
+
+void QQuickContext2DCommandBuffer::reset()
+{
+ cmdIdx = 0;
+ intIdx = 0;
+ boolIdx = 0;
+ realIdx = 0;
+ colorIdx = 0;
+ matrixIdx = 0;
+ brushIdx = 0;
+ pathIdx = 0;
+ imageIdx = 0;
+}
+
+QT_END_NAMESPACE
+
diff --git a/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h
new file mode 100644
index 0000000000..2029198648
--- /dev/null
+++ b/src/quick/items/context2d/qquickcontext2dcommandbuffer_p.h
@@ -0,0 +1,266 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKCONTEXT2DCOMMANDBUFFER_P_H
+#define QQUICKCONTEXT2DCOMMANDBUFFER_P_H
+
+#include "qquickcontext2d_p.h"
+#include <QtQuick/private/qdeclarativepixmapcache_p.h>
+
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickCanvasItem;
+class QMutex;
+
+class QQuickContext2DCommandBuffer
+{
+public:
+ QQuickContext2DCommandBuffer();
+ ~QQuickContext2DCommandBuffer();
+ void reset();
+ void clear();
+ inline int size() {return commands.size();}
+ inline bool isEmpty() const {return commands.isEmpty(); }
+ inline bool hasNext() const {return cmdIdx < commands.size(); }
+ inline QQuickContext2D::PaintCommand takeNextCommand() { return commands[cmdIdx++]; }
+
+ inline qreal takeGlobalAlpha() { return takeReal(); }
+ inline QPainter::CompositionMode takeGlobalCompositeOperation(){ return static_cast<QPainter::CompositionMode>(takeInt()); }
+ inline QBrush takeStrokeStyle() { return takeBrush(); }
+ inline QBrush takeFillStyle() { return takeBrush(); }
+
+ inline qreal takeLineWidth() { return takeReal(); }
+ inline Qt::PenCapStyle takeLineCap() { return static_cast<Qt::PenCapStyle>(takeInt());}
+ inline Qt::PenJoinStyle takeLineJoin(){ return static_cast<Qt::PenJoinStyle>(takeInt());}
+ inline qreal takeMiterLimit() { return takeReal(); }
+
+ inline void setGlobalAlpha( qreal alpha)
+ {
+ commands << QQuickContext2D::GlobalAlpha;
+ reals << alpha;
+ }
+
+ inline void setGlobalCompositeOperation(QPainter::CompositionMode cm)
+ {
+ commands << QQuickContext2D::GlobalCompositeOperation;
+ ints << cm;
+ }
+
+ inline void setStrokeStyle(const QBrush &style, bool repeatX = false, bool repeatY = false)
+ {
+ commands << QQuickContext2D::StrokeStyle;
+ brushes << style;
+ bools << repeatX << repeatY;
+ }
+
+ inline void drawImage(const QImage& image, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh)
+ {
+ commands << QQuickContext2D::DrawImage;
+ images << image;
+ reals << sx << sy << sw << sh << dx << dy << dw << dh;
+ }
+
+ inline qreal takeShadowOffsetX() { return takeReal(); }
+ inline qreal takeShadowOffsetY() { return takeReal(); }
+ inline qreal takeShadowBlur() { return takeReal(); }
+ inline QColor takeShadowColor() { return takeColor(); }
+
+
+ inline void updateMatrix(const QTransform& matrix)
+ {
+ commands << QQuickContext2D::UpdateMatrix;
+ matrixes << matrix;
+ }
+
+ inline void clearRect(qreal x, qreal y, qreal w, qreal h)
+ {
+ commands << QQuickContext2D::ClearRect;
+ reals << x << y << w << h;
+ }
+
+ inline void fillRect(qreal x, qreal y, qreal w, qreal h)
+ {
+ commands << QQuickContext2D::FillRect;
+ reals << x << y << w << h;
+ }
+
+ inline void strokeRect(qreal x, qreal y, qreal w, qreal h)
+ {
+ QPainterPath p;
+ p.addRect(x, y, w, h);
+
+ commands << QQuickContext2D::Stroke;
+ pathes << p;
+ }
+
+
+ inline void fill(const QPainterPath& path)
+ {
+ commands << QQuickContext2D::Fill;
+ pathes << path;
+
+ }
+
+ inline void stroke(const QPainterPath& path)
+ {
+ commands << QQuickContext2D::Stroke;
+ pathes << path;
+ }
+
+ inline void clip(const QPainterPath& path)
+ {
+ commands << QQuickContext2D::Clip;
+ pathes << path;
+ }
+
+
+
+ inline void setFillStyle(const QBrush &style, bool repeatX = false, bool repeatY = false)
+ {
+ commands << QQuickContext2D::FillStyle;
+ brushes << style;
+ bools << repeatX << repeatY;
+ }
+
+
+ inline void setLineWidth( qreal w)
+ {
+ commands << QQuickContext2D::LineWidth;
+ reals << w;
+ }
+
+ inline void setLineCap(Qt::PenCapStyle cap)
+ {
+ commands << QQuickContext2D::LineCap;
+ ints << cap;
+ }
+
+ inline void setLineJoin(Qt::PenJoinStyle join)
+ {
+ commands << QQuickContext2D::LineJoin;
+ ints << join;
+ }
+
+ inline void setMiterLimit( qreal limit)
+ {
+ commands << QQuickContext2D::MiterLimit;
+ reals << limit;
+ }
+
+ inline void setShadowOffsetX( qreal x)
+ {
+ commands << QQuickContext2D::ShadowOffsetX;
+ reals << x;
+ }
+
+ inline void setShadowOffsetY( qreal y)
+ {
+ commands << QQuickContext2D::ShadowOffsetY;
+ reals << y;
+ }
+
+ inline void setShadowBlur( qreal b)
+ {
+ commands << QQuickContext2D::ShadowBlur;
+ reals << b;
+ }
+
+ inline void setShadowColor(const QColor &color)
+ {
+ commands << QQuickContext2D::ShadowColor;
+ colors << color;
+ }
+
+ inline QTransform takeMatrix() { return matrixes[matrixIdx++]; }
+
+ // rects
+ inline QRectF takeRect() {
+ qreal x, y, w, h;
+ x = takeReal();
+ y = takeReal();
+ w = takeReal();
+ h = takeReal();
+ return QRectF(x, y, w ,h);
+ }
+
+ inline QPainterPath takePath() { return pathes[pathIdx++]; }
+
+ inline const QImage& takeImage() { return images[imageIdx++]; }
+
+ inline int takeInt() { return ints[intIdx++]; }
+ inline bool takeBool() {return bools[boolIdx++]; }
+ inline qreal takeReal() { return reals[realIdx++]; }
+ inline QColor takeColor() { return colors[colorIdx++]; }
+ inline QBrush takeBrush() { return brushes[brushIdx++]; }
+
+ void replay(QPainter* painter, QQuickContext2D::State& state);
+private:
+ QPen makePen(const QQuickContext2D::State& state);
+ void setPainterState(QPainter* painter, const QQuickContext2D::State& state, const QPen& pen);
+ int cmdIdx;
+ int intIdx;
+ int boolIdx;
+ int realIdx;
+ int colorIdx;
+ int matrixIdx;
+ int brushIdx;
+ int pathIdx;
+ int imageIdx;
+ QVector<QQuickContext2D::PaintCommand> commands;
+
+ QVector<int> ints;
+ QVector<bool> bools;
+ QVector<qreal> reals;
+ QVector<QColor> colors;
+ QVector<QTransform> matrixes;
+ QVector<QBrush> brushes;
+ QVector<QPainterPath> pathes;
+ QVector<QImage> images;
+};
+
+QT_END_HEADER
+
+QT_END_NAMESPACE
+
+#endif // QQUICKCONTEXT2DCOMMANDBUFFER_P_H
diff --git a/src/quick/items/context2d/qquickcontext2dnode.cpp b/src/quick/items/context2d/qquickcontext2dnode.cpp
new file mode 100644
index 0000000000..445cde8964
--- /dev/null
+++ b/src/quick/items/context2d/qquickcontext2dnode.cpp
@@ -0,0 +1,124 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickcontext2dnode_p.h"
+
+#include <QtQuick/private/qsgcontext_p.h>
+#include <QtCore/qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+
+QQuickContext2DNode::QQuickContext2DNode(QQuickCanvasItem* item)
+ : QSGGeometryNode()
+ , m_item(item)
+ , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
+ , m_texture(0)
+ , m_size(1, 1)
+ , m_dirtyGeometry(false)
+ , m_dirtyTexture(false)
+{
+ setMaterial(&m_materialO);
+ setOpaqueMaterial(&m_material);
+ setGeometry(&m_geometry);
+ setFlag(UsePreprocess, true);
+}
+
+QQuickContext2DNode::~QQuickContext2DNode()
+{
+ delete m_texture;
+}
+
+void QQuickContext2DNode::setSize(const QSizeF& size)
+{
+ if (m_size != size) {
+ m_dirtyGeometry = true;
+ m_size = size;
+ }
+}
+
+void QQuickContext2DNode::preprocess()
+{
+ bool doDirty = false;
+ QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(m_material.texture());
+ if (t) {
+ doDirty = t->updateTexture();
+ }
+ if (doDirty) {
+ m_dirtyTexture = true;
+ markDirty(DirtyMaterial);
+ }
+}
+void QQuickContext2DNode::setTexture(QQuickContext2DTexture* texture)
+{
+ if (texture != m_texture) {
+ m_dirtyTexture = true;
+ m_texture = texture;
+ }
+}
+
+void QQuickContext2DNode::update()
+{
+ if (m_dirtyGeometry)
+ updateGeometry();
+ if (m_dirtyTexture)
+ updateTexture();
+
+ m_dirtyGeometry = false;
+ m_dirtyTexture = false;
+}
+
+void QQuickContext2DNode::updateTexture()
+{
+ m_material.setTexture(m_texture);
+ m_materialO.setTexture(m_texture);
+ markDirty(DirtyMaterial);
+}
+
+void QQuickContext2DNode::updateGeometry()
+{
+ QRectF source = m_texture->textureSubRect();
+ QSGGeometry::updateTexturedRectGeometry(&m_geometry,
+ QRectF(0, 0, m_size.width(), m_size.height()),
+ source);
+ markDirty(DirtyGeometry);
+}
+QT_END_NAMESPACE
diff --git a/src/quick/items/context2d/qquickcontext2dnode_p.h b/src/quick/items/context2d/qquickcontext2dnode_p.h
new file mode 100644
index 0000000000..a00c939896
--- /dev/null
+++ b/src/quick/items/context2d/qquickcontext2dnode_p.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKCONTEXT2DNODE_P_H
+#define QQUICKCONTEXT2DNODE_P_H
+
+#include <QtQuick/qsgnode.h>
+#include <QtQuick/qsgtexturematerial.h>
+
+#include "qquickcanvasitem_p.h"
+#include "qquickcontext2dtexture_p.h"
+#include "qquickcontext2d_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickContext2DNode : public QSGGeometryNode
+{
+public:
+ QQuickContext2DNode(QQuickCanvasItem* item);
+ virtual ~QQuickContext2DNode();
+ void setTexture(QQuickContext2DTexture* texture);
+ void update();
+ void preprocess();
+ void setSize(const QSizeF& size);
+private:
+ void updateTexture();
+ void updateGeometry();
+
+ QQuickCanvasItem* m_item;
+ QSGOpaqueTextureMaterial m_material;
+ QSGTextureMaterial m_materialO;
+ QSGGeometry m_geometry;
+ QQuickContext2DTexture* m_texture;
+ QSizeF m_size;
+
+ bool m_dirtyGeometry;
+ bool m_dirtyTexture;
+};
+
+QT_END_HEADER
+
+QT_END_NAMESPACE
+
+#endif // QQUICKCONTEXT2DNODE_P_H
diff --git a/src/quick/items/context2d/qquickcontext2dtexture.cpp b/src/quick/items/context2d/qquickcontext2dtexture.cpp
new file mode 100644
index 0000000000..cbf1571189
--- /dev/null
+++ b/src/quick/items/context2d/qquickcontext2dtexture.cpp
@@ -0,0 +1,778 @@
+/****************************************************************************
+**
+** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickcontext2dtexture_p.h"
+#include "qquickcontext2dtile_p.h"
+#include "qquickcanvasitem_p.h"
+#include <private/qquickitem_p.h>
+#include <QtQuick/private/qsgtexture_p.h>
+#include "qquickcontext2dcommandbuffer_p.h"
+#include <QOpenGLPaintDevice>
+
+#include <QOpenGLFramebufferObject>
+#include <QOpenGLFramebufferObjectFormat>
+#include <QtCore/QThread>
+
+#define QT_MINIMUM_FBO_SIZE 64
+
+static inline int qt_next_power_of_two(int v)
+{
+ v--;
+ v |= v >> 1;
+ v |= v >> 2;
+ v |= v >> 4;
+ v |= v >> 8;
+ v |= v >> 16;
+ ++v;
+ return v;
+}
+
+
+Q_GLOBAL_STATIC(QThread, globalCanvasThreadRenderInstance)
+
+
+QQuickContext2DTexture::QQuickContext2DTexture()
+ : QSGDynamicTexture()
+ , m_context(0)
+ , m_item(0)
+ , m_canvasSize(QSize(1, 1))
+ , m_tileSize(QSize(1, 1))
+ , m_canvasWindow(QRect(0, 0, 1, 1))
+ , m_dirtyCanvas(false)
+ , m_dirtyTexture(false)
+ , m_threadRendering(false)
+ , m_smooth(false)
+ , m_tiledCanvas(false)
+ , m_doGrabImage(false)
+ , m_painting(false)
+{
+}
+
+QQuickContext2DTexture::~QQuickContext2DTexture()
+{
+ clearTiles();
+}
+
+QSize QQuickContext2DTexture::textureSize() const
+{
+ return m_canvasWindow.size();
+}
+
+void QQuickContext2DTexture::markDirtyTexture()
+{
+ lock();
+ m_dirtyTexture = true;
+ unlock();
+ emit textureChanged();
+}
+
+bool QQuickContext2DTexture::setCanvasSize(const QSize &size)
+{
+ if (m_canvasSize != size) {
+ m_canvasSize = size;
+ m_dirtyCanvas = true;
+ return true;
+ }
+ return false;
+}
+
+bool QQuickContext2DTexture::setTileSize(const QSize &size)
+{
+ if (m_tileSize != size) {
+ m_tileSize = size;
+ m_dirtyCanvas = true;
+ return true;
+ }
+ return false;
+}
+
+void QQuickContext2DTexture::setSmooth(bool smooth)
+{
+ m_smooth = smooth;
+}
+
+void QQuickContext2DTexture::setItem(QQuickCanvasItem* item)
+{
+ if (!item) {
+ lock();
+ m_item = 0;
+ m_context = 0;
+ unlock();
+ wake();
+ } else if (m_item != item) {
+ lock();
+ m_item = item;
+ m_context = item->context();
+ m_state = m_context->state;
+ unlock();
+ connect(this, SIGNAL(textureChanged()), m_item, SIGNAL(painted()));
+ }
+}
+
+bool QQuickContext2DTexture::setCanvasWindow(const QRect& r)
+{
+ if (m_canvasWindow != r) {
+ m_canvasWindow = r;
+ return true;
+ }
+ return false;
+}
+
+bool QQuickContext2DTexture::setDirtyRect(const QRect &r)
+{
+ bool doDirty = false;
+ if (m_tiledCanvas) {
+ foreach (QQuickContext2DTile* t, m_tiles) {
+ bool dirty = t->rect().intersected(r).isValid();
+ t->markDirty(dirty);
+ if (dirty)
+ doDirty = true;
+ }
+ } else {
+ doDirty = m_canvasWindow.intersected(r).isValid();
+ }
+ return doDirty;
+}
+
+void QQuickContext2DTexture::canvasChanged(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth)
+{
+ lock();
+
+ QSize ts = tileSize;
+ if (ts.width() > canvasSize.width())
+ ts.setWidth(canvasSize.width());
+
+ if (ts.height() > canvasSize.height())
+ ts.setHeight(canvasSize.height());
+
+ setCanvasSize(canvasSize);
+ setTileSize(ts);
+
+ if (canvasSize == canvasWindow.size()) {
+ m_tiledCanvas = false;
+ m_dirtyCanvas = false;
+ } else {
+ m_tiledCanvas = true;
+ }
+
+ bool doDirty = false;
+ if (dirtyRect.isValid())
+ doDirty = setDirtyRect(dirtyRect);
+
+ bool windowChanged = setCanvasWindow(canvasWindow);
+ if (windowChanged || doDirty) {
+ if (m_threadRendering)
+ QMetaObject::invokeMethod(this, "paint", Qt::QueuedConnection);
+ else if (supportDirectRendering()) {
+ QMetaObject::invokeMethod(this, "paint", Qt::DirectConnection);
+ }
+ }
+
+ setSmooth(smooth);
+ unlock();
+}
+
+void QQuickContext2DTexture::paintWithoutTiles()
+{
+ QQuickContext2DCommandBuffer* ccb = m_context->buffer();
+
+ if (ccb->isEmpty() && m_threadRendering && !m_doGrabImage) {
+ lock();
+ if (m_item)
+ QMetaObject::invokeMethod(m_item, "_doPainting", Qt::QueuedConnection, Q_ARG(QRectF, QRectF(0, 0, m_canvasSize.width(), m_canvasSize.height())));
+ wait();
+ unlock();
+ }
+ if (ccb->isEmpty()) {
+ return;
+ }
+
+ QPaintDevice* device = beginPainting();
+ if (!device) {
+ endPainting();
+ return;
+ }
+
+ QPainter p;
+ p.begin(device);
+ if (m_smooth)
+ p.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing
+ | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
+ else
+ p.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing
+ | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform, false);
+ p.setCompositionMode(QPainter::CompositionMode_SourceOver);
+ ccb->replay(&p, m_state);
+
+ ccb->clear();
+ markDirtyTexture();
+ endPainting();
+}
+
+bool QQuickContext2DTexture::canvasDestroyed()
+{
+ bool noCanvas = false;
+ lock();
+ noCanvas = m_item == 0;
+ unlock();
+ return noCanvas;
+}
+
+void QQuickContext2DTexture::paint()
+{
+ if (canvasDestroyed())
+ return;
+
+ if (!m_tiledCanvas) {
+ paintWithoutTiles();
+ } else {
+ lock();
+ QRect tiledRegion = createTiles(m_canvasWindow.intersected(QRect(QPoint(0, 0), m_canvasSize)));
+ unlock();
+
+ if (!tiledRegion.isEmpty()) {
+ if (m_threadRendering && !m_doGrabImage) {
+ QRect dirtyRect;
+
+ lock();
+ foreach (QQuickContext2DTile* tile, m_tiles) {
+ if (tile->dirty()) {
+ if (dirtyRect.isEmpty())
+ dirtyRect = tile->rect();
+ else
+ dirtyRect |= tile->rect();
+ }
+ }
+ unlock();
+
+ if (dirtyRect.isValid()) {
+ lock();
+ if (m_item)
+ QMetaObject::invokeMethod(m_item, "_doPainting", Qt::QueuedConnection, Q_ARG(QRectF, dirtyRect));
+ wait();
+ unlock();
+ }
+ }
+
+ if (beginPainting()) {
+ QQuickContext2D::State oldState = m_state;
+ QQuickContext2DCommandBuffer* ccb = m_context->buffer();
+ foreach (QQuickContext2DTile* tile, m_tiles) {
+ bool dirtyTile = false, dirtyCanvas = false, smooth = false;
+
+ lock();
+ dirtyTile = tile->dirty();
+ smooth = m_smooth;
+ dirtyCanvas = m_dirtyCanvas;
+ unlock();
+
+ //canvas size or tile size may change during painting tiles
+ if (dirtyCanvas) {
+ if (m_threadRendering)
+ QMetaObject::invokeMethod(this, "paint", Qt::QueuedConnection);
+ endPainting();
+ return;
+ } else if (dirtyTile) {
+ ccb->replay(tile->createPainter(smooth), oldState);
+ tile->drawFinished();
+ lock();
+ tile->markDirty(false);
+ unlock();
+ }
+
+ compositeTile(tile);
+ }
+ ccb->clear();
+ endPainting();
+ m_state = oldState;
+ markDirtyTexture();
+ }
+ }
+ }
+}
+
+QRect QQuickContext2DTexture::tiledRect(const QRectF& window, const QSize& tileSize)
+{
+ if (window.isEmpty())
+ return QRect();
+
+ const int tw = tileSize.width();
+ const int th = tileSize.height();
+ const int h1 = window.left() / tw;
+ const int v1 = window.top() / th;
+
+ const int htiles = ((window.right() - h1 * tw) + tw - 1)/tw;
+ const int vtiles = ((window.bottom() - v1 * th) + th - 1)/th;
+
+ return QRect(h1 * tw, v1 * th, htiles * tw, vtiles * th);
+}
+
+QRect QQuickContext2DTexture::createTiles(const QRect& window)
+{
+ QList<QQuickContext2DTile*> oldTiles = m_tiles;
+ m_tiles.clear();
+
+ if (window.isEmpty()) {
+ m_dirtyCanvas = false;
+ return QRect();
+ }
+
+ QRect r = tiledRect(window, m_tileSize);
+
+ const int tw = m_tileSize.width();
+ const int th = m_tileSize.height();
+ const int h1 = window.left() / tw;
+ const int v1 = window.top() / th;
+
+
+ const int htiles = r.width() / tw;
+ const int vtiles = r.height() / th;
+
+ for (int yy = 0; yy < vtiles; ++yy) {
+ for (int xx = 0; xx < htiles; ++xx) {
+ int ht = xx + h1;
+ int vt = yy + v1;
+
+ QQuickContext2DTile* tile = 0;
+
+ QPoint pos(ht * tw, vt * th);
+ QRect rect(pos, m_tileSize);
+
+ for (int i = 0; i < oldTiles.size(); i++) {
+ if (oldTiles[i]->rect() == rect) {
+ tile = oldTiles.takeAt(i);
+ break;
+ }
+ }
+
+ if (!tile)
+ tile = createTile();
+
+ tile->setRect(rect);
+ m_tiles.append(tile);
+ }
+ }
+
+ qDeleteAll(oldTiles);
+
+ m_dirtyCanvas = false;
+ return r;
+}
+
+void QQuickContext2DTexture::clearTiles()
+{
+ qDeleteAll(m_tiles);
+ m_tiles.clear();
+}
+
+QQuickContext2DFBOTexture::QQuickContext2DFBOTexture()
+ : QQuickContext2DTexture()
+ , m_fbo(0)
+ , m_multisampledFbo(0)
+ , m_paint_device(0)
+{
+ m_threadRendering = false;
+}
+
+QQuickContext2DFBOTexture::~QQuickContext2DFBOTexture()
+{
+ delete m_fbo;
+ delete m_multisampledFbo;
+ delete m_paint_device;
+}
+
+bool QQuickContext2DFBOTexture::setCanvasSize(const QSize &size)
+{
+ QSize s = QSize(qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(size.width()))
+ , qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(size.height())));
+
+ if (m_canvasSize != s) {
+ m_canvasSize = s;
+ m_dirtyCanvas = true;
+ return true;
+ }
+ return false;
+}
+
+bool QQuickContext2DFBOTexture::setTileSize(const QSize &size)
+{
+ QSize s = QSize(qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(size.width()))
+ , qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(size.height())));
+ if (m_tileSize != s) {
+ m_tileSize = s;
+ m_dirtyCanvas = true;
+ return true;
+ }
+ return false;
+}
+
+bool QQuickContext2DFBOTexture::setCanvasWindow(const QRect& canvasWindow)
+{
+ QSize s = QSize(qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(canvasWindow.size().width()))
+ , qMax(QT_MINIMUM_FBO_SIZE, qt_next_power_of_two(canvasWindow.size().height())));
+
+
+ bool doChanged = false;
+ if (m_fboSize != s) {
+ m_fboSize = s;
+ doChanged = true;
+ }
+
+ if (m_canvasWindow != canvasWindow)
+ m_canvasWindow = canvasWindow;
+
+ return doChanged;
+}
+
+void QQuickContext2DFBOTexture::bind()
+{
+ glBindTexture(GL_TEXTURE_2D, textureId());
+ updateBindOptions();
+}
+
+QRectF QQuickContext2DFBOTexture::textureSubRect() const
+{
+ return QRectF(0
+ , 0
+ , qreal(m_canvasWindow.width()) / m_fboSize.width()
+ , qreal(m_canvasWindow.height()) / m_fboSize.height());
+}
+
+
+int QQuickContext2DFBOTexture::textureId() const
+{
+ return m_fbo? m_fbo->texture() : 0;
+}
+
+
+bool QQuickContext2DFBOTexture::updateTexture()
+{
+ if (!m_context->buffer()->isEmpty()) {
+ paint();
+ }
+
+ bool textureUpdated = m_dirtyTexture;
+
+ m_dirtyTexture = false;
+
+ if (m_doGrabImage) {
+ grabImage();
+ m_condition.wakeOne();
+ m_doGrabImage = false;
+ }
+ return textureUpdated;
+}
+
+QQuickContext2DTile* QQuickContext2DFBOTexture::createTile() const
+{
+ return new QQuickContext2DFBOTile();
+}
+
+void QQuickContext2DFBOTexture::grabImage()
+{
+ if (m_fbo) {
+ m_grabedImage = m_fbo->toImage();
+ }
+}
+
+bool QQuickContext2DFBOTexture::doMultisampling() const
+{
+ static bool extensionsChecked = false;
+ static bool multisamplingSupported = false;
+
+ if (!extensionsChecked) {
+ QList<QByteArray> extensions = QByteArray((const char *)glGetString(GL_EXTENSIONS)).split(' ');
+ multisamplingSupported = extensions.contains("GL_EXT_framebuffer_multisample")
+ && extensions.contains("GL_EXT_framebuffer_blit");
+ extensionsChecked = true;
+ }
+
+ return multisamplingSupported && m_smooth;
+}
+
+QImage QQuickContext2DFBOTexture::toImage(const QRectF& region)
+{
+#define QML_CONTEXT2D_WAIT_MAX 5000
+
+ m_doGrabImage = true;
+ if (m_item)
+ m_item->update();
+
+ QImage grabbed;
+ m_mutex.lock();
+ bool ok = m_condition.wait(&m_mutex, QML_CONTEXT2D_WAIT_MAX);
+
+ if (!ok)
+ grabbed = QImage();
+
+ if (region.isValid())
+ grabbed = m_grabedImage.copy(region.toRect());
+ else
+ grabbed = m_grabedImage;
+ m_grabedImage = QImage();
+ return grabbed;
+}
+
+void QQuickContext2DFBOTexture::compositeTile(QQuickContext2DTile* tile)
+{
+ QQuickContext2DFBOTile* t = static_cast<QQuickContext2DFBOTile*>(tile);
+ QRect target = t->rect().intersect(m_canvasWindow);
+ if (target.isValid()) {
+ QRect source = target;
+
+ source.moveTo(source.topLeft() - t->rect().topLeft());
+ target.moveTo(target.topLeft() - m_canvasWindow.topLeft());
+
+ QOpenGLFramebufferObject::blitFramebuffer(m_fbo, target, t->fbo(), source);
+ }
+}
+QQuickCanvasItem::RenderTarget QQuickContext2DFBOTexture::renderTarget() const
+{
+ return QQuickCanvasItem::FramebufferObject;
+}
+QPaintDevice* QQuickContext2DFBOTexture::beginPainting()
+{
+ QQuickContext2DTexture::beginPainting();
+
+ if (m_canvasWindow.size().isEmpty() && !m_threadRendering) {
+ delete m_fbo;
+ delete m_multisampledFbo;
+ m_fbo = 0;
+ m_multisampledFbo = 0;
+ return 0;
+ } else if (!m_fbo || m_fbo->size() != m_fboSize) {
+ delete m_fbo;
+ delete m_multisampledFbo;
+ if (doMultisampling()) {
+ {
+ QOpenGLFramebufferObjectFormat format;
+ format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
+ format.setSamples(8);
+ m_multisampledFbo = new QOpenGLFramebufferObject(m_fboSize, format);
+ }
+ {
+ QOpenGLFramebufferObjectFormat format;
+ format.setAttachment(QOpenGLFramebufferObject::NoAttachment);
+ m_fbo = new QOpenGLFramebufferObject(m_fboSize, format);
+ }
+ } else {
+ QOpenGLFramebufferObjectFormat format;
+ format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
+
+ m_fbo = new QOpenGLFramebufferObject(m_fboSize, format);
+ }
+ }
+
+ if (doMultisampling())
+ m_multisampledFbo->bind();
+ else
+ m_fbo->bind();
+
+
+ if (!m_paint_device) {
+ QOpenGLPaintDevice *gl_device = new QOpenGLPaintDevice(m_fbo->size());
+ gl_device->setPaintFlipped(true);
+ m_paint_device = gl_device;
+ }
+
+ return m_paint_device;
+}
+
+void QQuickContext2DFBOTexture::endPainting()
+{
+ QQuickContext2DTexture::endPainting();
+ if (m_multisampledFbo) {
+ QOpenGLFramebufferObject::blitFramebuffer(m_fbo, m_multisampledFbo);
+ m_multisampledFbo->release();
+ } else if (m_fbo)
+ m_fbo->release();
+}
+void qt_quit_context2d_render_thread()
+{
+ QThread* thread = globalCanvasThreadRenderInstance();
+
+ if (thread->isRunning()) {
+ thread->exit(0);
+ thread->wait(1000);
+ }
+}
+
+QQuickContext2DImageTexture::QQuickContext2DImageTexture(bool threadRendering)
+ : QQuickContext2DTexture()
+ , m_texture(new QSGPlainTexture())
+{
+ m_texture->setOwnsTexture(true);
+ m_texture->setHasMipmaps(false);
+
+ m_threadRendering = threadRendering;
+
+ if (m_threadRendering) {
+ QThread* thread = globalCanvasThreadRenderInstance();
+ moveToThread(thread);
+
+ if (!thread->isRunning()) {
+ qAddPostRoutine(qt_quit_context2d_render_thread);
+ thread->start();
+ }
+ }
+}
+
+QQuickContext2DImageTexture::~QQuickContext2DImageTexture()
+{
+ delete m_texture;
+}
+
+int QQuickContext2DImageTexture::textureId() const
+{
+ return m_texture->textureId();
+}
+
+void QQuickContext2DImageTexture::lock()
+{
+ if (m_threadRendering)
+ m_mutex.lock();
+}
+void QQuickContext2DImageTexture::unlock()
+{
+ if (m_threadRendering)
+ m_mutex.unlock();
+}
+
+void QQuickContext2DImageTexture::wait()
+{
+ if (m_threadRendering)
+ m_waitCondition.wait(&m_mutex);
+}
+
+void QQuickContext2DImageTexture::wake()
+{
+ if (m_threadRendering)
+ m_waitCondition.wakeOne();
+}
+
+bool QQuickContext2DImageTexture::supportDirectRendering() const
+{
+ return !m_threadRendering;
+}
+
+QQuickCanvasItem::RenderTarget QQuickContext2DImageTexture::renderTarget() const
+{
+ return QQuickCanvasItem::Image;
+}
+
+void QQuickContext2DImageTexture::bind()
+{
+ m_texture->bind();
+}
+
+bool QQuickContext2DImageTexture::updateTexture()
+{
+ lock();
+ bool textureUpdated = m_dirtyTexture;
+ if (m_dirtyTexture) {
+ m_texture->setImage(m_image);
+ m_dirtyTexture = false;
+ }
+ unlock();
+ return textureUpdated;
+}
+
+QQuickContext2DTile* QQuickContext2DImageTexture::createTile() const
+{
+ return new QQuickContext2DImageTile();
+}
+
+void QQuickContext2DImageTexture::grabImage(const QRect& r)
+{
+ m_doGrabImage = true;
+ paint();
+ m_doGrabImage = false;
+ m_grabedImage = m_image.copy(r);
+}
+
+QImage QQuickContext2DImageTexture::toImage(const QRectF& region)
+{
+ QRect r = region.isValid() ? region.toRect() : QRect(QPoint(0, 0), m_canvasWindow.size());
+ if (threadRendering()) {
+ wake();
+ QMetaObject::invokeMethod(this, "grabImage", Qt::BlockingQueuedConnection, Q_ARG(QRect, r));
+ } else {
+ QMetaObject::invokeMethod(this, "grabImage", Qt::DirectConnection, Q_ARG(QRect, r));
+ }
+ QImage image = m_grabedImage;
+ m_grabedImage = QImage();
+ return image;
+}
+
+QPaintDevice* QQuickContext2DImageTexture::beginPainting()
+{
+ QQuickContext2DTexture::beginPainting();
+
+ if (m_canvasWindow.size().isEmpty())
+ return 0;
+
+ lock();
+ if (m_image.size() != m_canvasWindow.size()) {
+ m_image = QImage(m_canvasWindow.size(), QImage::Format_ARGB32_Premultiplied);
+ m_image.fill(0x00000000);
+ }
+ unlock();
+ return &m_image;
+}
+
+void QQuickContext2DImageTexture::compositeTile(QQuickContext2DTile* tile)
+{
+ Q_ASSERT(!tile->dirty());
+ QQuickContext2DImageTile* t = static_cast<QQuickContext2DImageTile*>(tile);
+ QRect target = t->rect().intersect(m_canvasWindow);
+ if (target.isValid()) {
+ QRect source = target;
+ source.moveTo(source.topLeft() - t->rect().topLeft());
+ target.moveTo(target.topLeft() - m_canvasWindow.topLeft());
+
+ lock();
+ m_painter.begin(&m_image);
+ m_painter.setCompositionMode(QPainter::CompositionMode_Source);
+ m_painter.drawImage(target, t->image(), source);
+ m_painter.end();
+ unlock();
+ }
+}
diff --git a/src/quick/items/context2d/qquickcontext2dtexture_p.h b/src/quick/items/context2d/qquickcontext2dtexture_p.h
new file mode 100644
index 0000000000..d055db7c06
--- /dev/null
+++ b/src/quick/items/context2d/qquickcontext2dtexture_p.h
@@ -0,0 +1,203 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKCONTEXT2DTEXTURE_P_H
+#define QQUICKCONTEXT2DTEXTURE_P_H
+
+#include <QtQuick/qsgtexture.h>
+#include "qquickcanvasitem_p.h"
+#include "qquickcontext2d_p.h"
+
+#include <QOpenGLContext>
+#include <QOpenGLFramebufferObject>
+
+#include <QtCore/QMutex>
+#include <QtCore/QWaitCondition>
+#include <QtCore/QThread>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickContext2DTile;
+class QQuickContext2DCommandBuffer;
+
+class QQuickContext2DTexture : public QSGDynamicTexture
+{
+ Q_OBJECT
+public:
+ QQuickContext2DTexture();
+ ~QQuickContext2DTexture();
+
+ virtual bool hasAlphaChannel() const {return true;}
+ virtual bool hasMipmaps() const {return false;}
+ virtual QSize textureSize() const;
+ virtual void lock() {}
+ virtual void unlock() {}
+ virtual void wait() {}
+ virtual void wake() {}
+ bool threadRendering() const {return m_threadRendering;}
+ virtual bool supportThreadRendering() const = 0;
+ virtual bool supportDirectRendering() const = 0;
+ virtual QQuickCanvasItem::RenderTarget renderTarget() const = 0;
+ virtual QImage toImage(const QRectF& region = QRectF()) = 0;
+ static QRect tiledRect(const QRectF& window, const QSize& tileSize);
+
+ virtual bool setCanvasSize(const QSize &size);
+ virtual bool setTileSize(const QSize &size);
+ virtual bool setCanvasWindow(const QRect& canvasWindow);
+ void setSmooth(bool smooth);
+ bool setDirtyRect(const QRect &dirtyRect);
+ virtual void canvasChanged(const QSize& canvasSize, const QSize& tileSize, const QRect& canvasWindow, const QRect& dirtyRect, bool smooth);
+ bool canvasDestroyed();
+Q_SIGNALS:
+ void textureChanged();
+
+public Q_SLOTS:
+ void markDirtyTexture();
+ void setItem(QQuickCanvasItem* item);
+ void paint();
+
+protected:
+ void paintWithoutTiles();
+ virtual QPaintDevice* beginPainting() {m_painting = true; return 0; }
+ virtual void endPainting() {m_painting = false;}
+ virtual QQuickContext2DTile* createTile() const = 0;
+ virtual void compositeTile(QQuickContext2DTile* tile) = 0;
+
+ void clearTiles();
+ QRect createTiles(const QRect& window);
+
+ QList<QQuickContext2DTile*> m_tiles;
+ QQuickContext2D* m_context;
+
+ QQuickContext2D::State m_state;
+
+ QQuickCanvasItem* m_item;
+ QSize m_canvasSize;
+ QSize m_tileSize;
+ QRect m_canvasWindow;
+
+ uint m_dirtyCanvas : 1;
+ uint m_dirtyTexture : 1;
+ uint m_threadRendering : 1;
+ uint m_smooth : 1;
+ uint m_tiledCanvas : 1;
+ uint m_doGrabImage : 1;
+ uint m_painting : 1;
+};
+
+class QQuickContext2DFBOTexture : public QQuickContext2DTexture
+{
+ Q_OBJECT
+
+public:
+ QQuickContext2DFBOTexture();
+ ~QQuickContext2DFBOTexture();
+ virtual int textureId() const;
+ virtual bool updateTexture();
+ virtual QQuickContext2DTile* createTile() const;
+ virtual QImage toImage(const QRectF& region = QRectF());
+ virtual QPaintDevice* beginPainting();
+ virtual void endPainting();
+ QRectF textureSubRect() const;
+ virtual bool supportThreadRendering() const {return false;}
+ virtual bool supportDirectRendering() const {return false;}
+ virtual QQuickCanvasItem::RenderTarget renderTarget() const;
+ virtual void compositeTile(QQuickContext2DTile* tile);
+ virtual void bind();
+ virtual bool setCanvasSize(const QSize &size);
+ virtual bool setTileSize(const QSize &size);
+ virtual bool setCanvasWindow(const QRect& canvasWindow);
+private Q_SLOTS:
+ void grabImage();
+
+private:
+ bool doMultisampling() const;
+ QImage m_grabedImage;
+ QOpenGLFramebufferObject *m_fbo;
+ QOpenGLFramebufferObject *m_multisampledFbo;
+ QMutex m_mutex;
+ QWaitCondition m_condition;
+ QSize m_fboSize;
+ QPaintDevice *m_paint_device;
+};
+
+class QSGPlainTexture;
+class QQuickContext2DImageTexture : public QQuickContext2DTexture
+{
+ Q_OBJECT
+
+public:
+ QQuickContext2DImageTexture(bool threadRendering = true);
+ ~QQuickContext2DImageTexture();
+ virtual int textureId() const;
+ virtual void bind();
+ virtual bool supportThreadRendering() const {return true;}
+ virtual bool supportDirectRendering() const;
+ virtual QQuickCanvasItem::RenderTarget renderTarget() const;
+ virtual void lock();
+ virtual void unlock();
+ virtual void wait();
+ virtual void wake();
+
+ virtual bool updateTexture();
+ virtual QQuickContext2DTile* createTile() const;
+ virtual QImage toImage(const QRectF& region = QRectF());
+ virtual QPaintDevice* beginPainting();
+ virtual void compositeTile(QQuickContext2DTile* tile);
+
+private Q_SLOTS:
+ void grabImage(const QRect& r);
+private:
+ QImage m_image;
+ QImage m_grabedImage;
+ QMutex m_mutex;
+ QWaitCondition m_waitCondition;
+ QPainter m_painter;
+ QSGPlainTexture* m_texture;
+};
+
+QT_END_HEADER
+
+QT_END_NAMESPACE
+
+#endif // QQUICKCONTEXT2DTEXTURE_P_H
diff --git a/src/quick/items/context2d/qquickcontext2dtile.cpp b/src/quick/items/context2d/qquickcontext2dtile.cpp
new file mode 100644
index 0000000000..6217c66094
--- /dev/null
+++ b/src/quick/items/context2d/qquickcontext2dtile.cpp
@@ -0,0 +1,165 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qquickcontext2dtile_p.h"
+
+#include <QOpenGLFramebufferObject>
+#include <QOpenGLFramebufferObjectFormat>
+#include <QOpenGLPaintDevice>
+
+QQuickContext2DTile::QQuickContext2DTile()
+ : m_dirty(true)
+ , m_rect(QRect(0, 0, 1, 1))
+ , m_device(0)
+{
+}
+
+QQuickContext2DTile::~QQuickContext2DTile()
+{
+ if (m_painter.isActive())
+ m_painter.end();
+}
+
+QPainter* QQuickContext2DTile::createPainter(bool smooth)
+{
+ if (m_painter.isActive())
+ m_painter.end();
+
+ if (m_device) {
+ aboutToDraw();
+ m_painter.begin(m_device);
+ m_painter.resetTransform();
+ m_painter.setCompositionMode(QPainter::CompositionMode_Source);
+
+#ifdef QQUICKCONTEXT2D_DEBUG
+ int v = 100;
+ int gray = (m_rect.x() / m_rect.width() + m_rect.y() / m_rect.height()) % 2;
+ if (gray)
+ v = 150;
+ m_painter.fillRect(QRect(0, 0, m_rect.width(), m_rect.height()), QColor(v, v, v, 255));
+#endif
+ if (smooth)
+ m_painter.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing
+ | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
+ else
+ m_painter.setRenderHints(QPainter::Antialiasing | QPainter::HighQualityAntialiasing
+ | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform, false);
+
+ m_painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
+ m_painter.translate(-m_rect.left(), -m_rect.top());
+ m_painter.setClipRect(m_rect);
+ m_painter.setClipping(false);
+ return &m_painter;
+ }
+
+ return 0;
+}
+
+QQuickContext2DFBOTile::QQuickContext2DFBOTile()
+ : QQuickContext2DTile()
+ , m_fbo(0)
+{
+}
+
+
+QQuickContext2DFBOTile::~QQuickContext2DFBOTile()
+{
+ delete m_fbo;
+}
+
+void QQuickContext2DFBOTile::aboutToDraw()
+{
+ m_fbo->bind();
+ if (!m_device) {
+ QOpenGLPaintDevice *gl_device = new QOpenGLPaintDevice(rect().size());
+ m_device = gl_device;
+ QPainter p(m_device);
+ p.fillRect(QRectF(0, 0, m_fbo->width(), m_fbo->height()), QColor(qRgba(0, 0, 0, 0)));
+ p.end();
+ }
+}
+
+void QQuickContext2DFBOTile::drawFinished()
+{
+ m_fbo->release();
+}
+
+void QQuickContext2DFBOTile::setRect(const QRect& r)
+{
+ if (m_rect == r)
+ return;
+ m_rect = r;
+ m_dirty = true;
+ if (!m_fbo || m_fbo->size() != r.size()) {
+ QOpenGLFramebufferObjectFormat format;
+ format.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil);
+ format.setInternalTextureFormat(GL_RGBA);
+ format.setMipmap(false);
+
+ if (m_painter.isActive())
+ m_painter.end();
+
+ delete m_fbo;
+ m_fbo = new QOpenGLFramebufferObject(r.size(), format);
+ }
+}
+
+
+QQuickContext2DImageTile::QQuickContext2DImageTile()
+ : QQuickContext2DTile()
+{
+}
+
+QQuickContext2DImageTile::~QQuickContext2DImageTile()
+{
+}
+
+void QQuickContext2DImageTile::setRect(const QRect& r)
+{
+ if (m_rect == r)
+ return;
+ m_rect = r;
+ m_dirty = true;
+ if (m_image.size() != r.size()) {
+ m_image = QImage(r.size(), QImage::Format_ARGB32_Premultiplied);
+ }
+ m_device = &m_image;
+}
diff --git a/src/quick/items/context2d/qquickcontext2dtile_p.h b/src/quick/items/context2d/qquickcontext2dtile_p.h
new file mode 100644
index 0000000000..a38384d3c7
--- /dev/null
+++ b/src/quick/items/context2d/qquickcontext2dtile_p.h
@@ -0,0 +1,110 @@
+/****************************************************************************
+**
+** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
+** All rights reserved.
+** Contact: Nokia Corporation (qt-info@nokia.com)
+**
+** This file is part of the QtDeclarative module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** GNU Lesser General Public License Usage
+** This file may be used under the terms of the GNU Lesser General Public
+** License version 2.1 as published by the Free Software Foundation and
+** appearing in the file LICENSE.LGPL included in the packaging of this
+** file. Please review the following information to ensure the GNU Lesser
+** General Public License version 2.1 requirements will be met:
+** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Nokia gives you certain additional
+** rights. These rights are described in the Nokia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU General
+** Public License version 3.0 as published by the Free Software Foundation
+** and appearing in the file LICENSE.GPL included in the packaging of this
+** file. Please review the following information to ensure the GNU General
+** Public License version 3.0 requirements will be met:
+** http://www.gnu.org/copyleft/gpl.html.
+**
+** Other Usage
+** Alternatively, this file may be used in accordance with the terms and
+** conditions contained in a signed written agreement between you and Nokia.
+**
+**
+**
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QQUICKCONTEXT2DTILE_P_H
+#define QQUICKCONTEXT2DTILE_P_H
+
+#include "qquickcontext2d_p.h"
+#include <QOpenGLFramebufferObject>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+class QQuickContext2DTexture;
+class QQuickContext2DCommandBuffer;
+
+class QQuickContext2DTile
+{
+public:
+ QQuickContext2DTile();
+ ~QQuickContext2DTile();
+
+ bool dirty() const {return m_dirty;}
+ void markDirty(bool dirty) {m_dirty = dirty;}
+
+ QRect rect() const {return m_rect;}
+
+ virtual void setRect(const QRect& r) = 0;
+ virtual QPainter* createPainter(bool smooth = false);
+ virtual void drawFinished() {}
+
+protected:
+ virtual void aboutToDraw() {}
+ uint m_dirty : 1;
+ QRect m_rect;
+ QPaintDevice* m_device;
+ QPainter m_painter;
+};
+
+
+class QQuickContext2DFBOTile : public QQuickContext2DTile
+{
+public:
+ QQuickContext2DFBOTile();
+ ~QQuickContext2DFBOTile();
+ virtual void setRect(const QRect& r);
+ QOpenGLFramebufferObject* fbo() const {return m_fbo;}
+ void drawFinished();
+
+protected:
+ void aboutToDraw();
+private:
+
+
+ QOpenGLFramebufferObject *m_fbo;
+};
+
+class QQuickContext2DImageTile : public QQuickContext2DTile
+{
+public:
+ QQuickContext2DImageTile();
+ ~QQuickContext2DImageTile();
+ void setRect(const QRect& r);
+ const QImage& image() const {return m_image;}
+private:
+ QImage m_image;
+};
+QT_END_HEADER
+
+QT_END_NAMESPACE
+
+#endif // QQUICKCONTEXT2DTILE_P_H