aboutsummaryrefslogtreecommitdiffstats
path: root/src/declarative/items/context2d
diff options
context:
space:
mode:
authorCharles Yin <charles.yin@nokia.com>2011-08-09 16:44:38 +1000
committerQt by Nokia <qt-info@nokia.com>2011-09-12 17:04:03 +0200
commit82b21536e72a640699594b3e82a5a6182a95521c (patch)
treed641802de88c644e12b9cf3e2279f3aae084779b /src/declarative/items/context2d
parent6a15dae8c2f09c99bb124b4587ff8088b35a7273 (diff)
canvas item refactors
1.Supports tiled canvas with canvasSize, tileSize and canvasWindow 2.Supports different rendering targets: Canvas.Image and Canvas.FrameBufferObject by renderTarget property 3.Supports thread rendering when possible by threadRendering property. 4.Refactors QSGContext2D code, move some logic to QSGContext2DCommandBuffer,QSGContext2DTexture,QSGContext2DTile, etc 5.Updates/adds some canvas examples 6.Some improvements for context2d API 6.1 drawImage() now loads image asynchoronously and draw images automatically when they are ready 6.2 adds fillRule supports 6.3 add svg path supports 6.4 Pixel operations (getImageData/putImageData/createImageData) now have better performance by using V8 indexed array accessors 6.5 Uses QTransform instead of QMatrix 6.6 Gradients/patterns now are V8 values, not QObjects 6.7 Supports measureText and TextMetrics interface 6.8 Gives not support warnings for unimplemented functions (drawFocusRing,setCaretSelectionRect,caretBlinkRate) 6.9 Better error handling, throw standard DOM exceptions according to the HTML5 context2d spec. 6.10 Adds shear, resetTransform to matrix operations 6.11 Adds roundedRect, ellipse, text to path operations 6.12 Adds new features to CanvasImageData interface 1) adds mirror() function 2) adds filter() function, include the following filters: Threshold GrayScale Brightness Invert Blur Blend Opaque Convolute 7. Adds documentations Change-Id: Id19224260d6a3fdc589d1f9681c34a88a7e7b3e5 Reviewed-on: http://codereview.qt-project.org/3621 Reviewed-by: Charles Yin <charles.yin@nokia.com>
Diffstat (limited to 'src/declarative/items/context2d')
-rw-r--r--src/declarative/items/context2d/context2d.pri9
-rw-r--r--src/declarative/items/context2d/qsgcanvasitem.cpp529
-rw-r--r--src/declarative/items/context2d/qsgcanvasitem_p.h91
-rw-r--r--src/declarative/items/context2d/qsgcontext2d.cpp4033
-rw-r--r--src/declarative/items/context2d/qsgcontext2d_p.h335
-rw-r--r--src/declarative/items/context2d/qsgcontext2d_p_p.h238
-rw-r--r--src/declarative/items/context2d/qsgcontext2dcommandbuffer.cpp402
-rw-r--r--src/declarative/items/context2d/qsgcontext2dcommandbuffer_p.h268
-rw-r--r--src/declarative/items/context2d/qsgcontext2dnode.cpp116
-rw-r--r--src/declarative/items/context2d/qsgcontext2dnode_p.h84
-rw-r--r--src/declarative/items/context2d/qsgcontext2dtexture.cpp673
-rw-r--r--src/declarative/items/context2d/qsgcontext2dtexture_p.h200
-rw-r--r--src/declarative/items/context2d/qsgcontext2dtile.cpp146
-rw-r--r--src/declarative/items/context2d/qsgcontext2dtile_p.h104
14 files changed, 4381 insertions, 2847 deletions
diff --git a/src/declarative/items/context2d/context2d.pri b/src/declarative/items/context2d/context2d.pri
index a43ef7806a..60fe28dd43 100644
--- a/src/declarative/items/context2d/context2d.pri
+++ b/src/declarative/items/context2d/context2d.pri
@@ -3,9 +3,16 @@ INCLUDEPATH += $$PWD
SOURCES += \
$$PWD/qsgcanvasitem.cpp \
$$PWD/qsgcontext2d.cpp \
+ $$PWD/qsgcontext2dnode.cpp \
+ $$PWD/qsgcontext2dtile.cpp \
+ $$PWD/qsgcontext2dtexture.cpp \
+ $$PWD/qsgcontext2dcommandbuffer.cpp \
HEADERS += \
$$PWD/qsgcanvasitem_p.h \
- $$PWD/qsgcontext2d_p_p.h \
$$PWD/qsgcontext2d_p.h \
+ $$PWD/qsgcontext2dnode_p.h \
+ $$PWD/qsgcontext2dtile_p.h \
+ $$PWD/qsgcontext2dtexture_p.h \
+ $$PWD/qsgcontext2dcommandbuffer_p.h \
diff --git a/src/declarative/items/context2d/qsgcanvasitem.cpp b/src/declarative/items/context2d/qsgcanvasitem.cpp
index 0bd9a47d8d..03eab72368 100644
--- a/src/declarative/items/context2d/qsgcanvasitem.cpp
+++ b/src/declarative/items/context2d/qsgcanvasitem.cpp
@@ -39,124 +39,366 @@
**
****************************************************************************/
-#include <QtGui/qpainter.h>
-
#include "private/qsgadaptationlayer_p.h"
#include "qsgcanvasitem_p.h"
-#include "qsgpainteditem_p.h"
+#include "qsgitem_p.h"
#include "qsgcontext2d_p.h"
-#include "private/qsgpainternode_p.h"
+#include "qsgcontext2dnode_p.h"
+#include "qsgcontext2dtexture_p.h"
+#include "qdeclarativepixmapcache_p.h"
+
#include <qdeclarativeinfo.h>
#include "qdeclarativeengine_p.h"
#include <QtCore/QBuffer>
QT_BEGIN_NAMESPACE
-class QSGCanvasItemPrivate : public QSGPaintedItemPrivate
+class QSGCanvasItemPrivate : public QSGItemPrivate
{
public:
QSGCanvasItemPrivate();
~QSGCanvasItemPrivate();
QSGContext2D* context;
- QList<QRect> dirtyRegions;
- QRect unitedDirtyRegion;
- qreal canvasX;
- qreal canvasY;
+ QSGContext2DTexture* texture;
+ QSizeF canvasSize;
+ QSize tileSize;
+ QRectF canvasWindow;
+ QRectF dirtyRect;
+ uint threadRendering : 1;
+ uint hasCanvasSize :1;
+ uint hasTileSize :1;
+ uint hasCanvasWindow :1;
+ uint componentCompleted :1;
+ QSGCanvasItem::RenderTarget renderTarget;
+ QHash<QUrl, QDeclarativePixmap*> images;
+ QUrl baseUrl;
};
-
-/*!
- \internal
-*/
QSGCanvasItemPrivate::QSGCanvasItemPrivate()
- : QSGPaintedItemPrivate()
+ : QSGItemPrivate()
, context(0)
- , unitedDirtyRegion()
- , canvasX(0.)
- , canvasY(0.)
+ , texture(0)
+ , canvasSize(1, 1)
+ , tileSize(1, 1)
+ , threadRendering(true)
+ , hasCanvasSize(false)
+ , hasTileSize(false)
+ , hasCanvasWindow(false)
+ , componentCompleted(false)
+ , renderTarget(QSGCanvasItem::Image)
{
}
QSGCanvasItemPrivate::~QSGCanvasItemPrivate()
{
+ qDeleteAll(images);
+}
+
+/*!
+ \qmlclass Canvas QSGCanvasItem
+ \inqmlmodule QtQuick 2
+ \since QtQuick 2.0
+ \brief The Canvas item provides HTML5 canvas element compatible scripts with a resolution-dependent bitmap canvas.
+ \inherits Item
+ \ingroup qml-basic-visual-elements
+
+ The canvas is used to render graphs, game graphics, or other visual images on the fly.
+
+ \section1 Example Usage
+
+ \section1 Thread Rendering Mode
+
+ \section1 Tiled Canvas
+
+ \section1 Quality and Performance
+
+ By default, all of the drawing commands are rendered by a dedicated thread for better
+ performance and avoid blocking the main GUI thread. Setting the \l threadRendering property
+ to false can make the canvas rendering stay in the main GUI thread.
+
+ \sa Context2D
+*/
+
+/*!
+ Constructs a QSGCanvasItem with the given \a parent item.
+ */
+QSGCanvasItem::QSGCanvasItem(QSGItem *parent)
+ : QSGItem(*(new QSGCanvasItemPrivate), parent)
+{
+ setFlag(ItemHasContents);
+}
+
+/*!
+ Destroys the QSGCanvasItem.
+*/
+QSGCanvasItem::~QSGCanvasItem()
+{
+ Q_D(QSGCanvasItem);
+ if (d->texture) {
+ d->texture->setItem(0);
+ d->texture->deleteLater();
+ }
+ 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 with the current canvas item size.
+ \sa tileSize canvasWindow
+*/
+QSizeF QSGCanvasItem::canvasSize() const
+{
+ Q_D(const QSGCanvasItem);
+ return d->canvasSize;
+}
+
+void QSGCanvasItem::setCanvasSize(const QSizeF & size)
+{
+ Q_D(QSGCanvasItem);
+ if (d->canvasSize != size) {
+ d->hasCanvasSize = true;
+ d->canvasSize = size;
+ emit canvasSizeChanged();
+ polish();
+ }
}
+/*!
+ \qmlproperty size QtQuick2::Canvas::tileSize
+ Holds the canvas rendering tile size.
+
+ The canvas render can improve the rendering performance
+ by rendering and caching each tiles instead of rendering
+ the whole canvas everytime.
+
+ Additionally, the canvas size could be infinitely large
+ because only those tiles within the current visible region
+ are actually rendered.
+
+ By default, the tile size is the same with the canvas size.
+ \sa canvasSize
+*/
+QSize QSGCanvasItem::tileSize() const
+{
+ Q_D(const QSGCanvasItem);
+ return d->tileSize;
+}
-void QSGCanvasItem::setCanvasX(qreal x)
+void QSGCanvasItem::setTileSize(const QSize & size)
{
Q_D(QSGCanvasItem);
- if (d->canvasX != x) {
- d->canvasX = x;
- emit canvasXChanged();
+ if (d->tileSize != size) {
+ d->hasTileSize = true;
+ d->tileSize = size;
+
+ emit tileSizeChanged();
+ polish();
}
}
-void QSGCanvasItem::setCanvasY(qreal y)
+
+/*!
+ \qmlproperty rect QtQuick2::Canvas::canvasWindow
+ Holds the current canvas visible window.
+
+ This property is read only, a canvas window can
+ be changed by changing the canvas item width, height
+ or the canvas viewport properties.
+
+ When painting on a canvas item, even the item is visible
+ and focused, only the current canvas window area is actually
+ rendered even the painting commands may paint shaps out of
+ the canvas window.
+
+ The canvas window size is already synchronized with the canvas item size.
+ \sa width height canvasSize
+*/
+QRectF QSGCanvasItem::canvasWindow() const
+{
+ Q_D(const QSGCanvasItem);
+ return d->canvasWindow;
+}
+
+void QSGCanvasItem::setCanvasWindow(const QRectF& rect)
{
Q_D(QSGCanvasItem);
- if (d->canvasY != y) {
- d->canvasY = y;
- emit canvasYChanged();
+ if (d->canvasWindow != rect) {
+ d->canvasWindow = rect;
+
+ d->hasCanvasWindow = true;
+ emit canvasWindowChanged();
+ polish();
}
}
-qreal QSGCanvasItem::canvasX() const
+
+QSGContext2D* QSGCanvasItem::context() const
{
Q_D(const QSGCanvasItem);
- return d->canvasX;
+ return d->context;
}
-qreal QSGCanvasItem::canvasY() const
+bool QSGCanvasItem::threadRendering() const
{
Q_D(const QSGCanvasItem);
- return d->canvasY;
+ return d->threadRendering;
}
-QPointF QSGCanvasItem::canvasPos() const
+
+QSGCanvasItem::RenderTarget QSGCanvasItem::renderTarget() const
{
Q_D(const QSGCanvasItem);
- return QPointF(d->canvasX, d->canvasY);
+ return d->renderTarget;
}
-/*!
- Constructs a QSGCanvasItem with the given \a parent item.
- */
-QSGCanvasItem::QSGCanvasItem(QSGItem *parent)
- : QSGPaintedItem(*(new QSGCanvasItemPrivate), parent)
+void QSGCanvasItem::setRenderTarget(RenderTarget target)
{
+ Q_D(QSGCanvasItem);
+ if (d->renderTarget != target) {
+ d->renderTarget = target;
+
+ if (d->componentCompleted)
+ createTexture();
+ emit renderTargetChanged();
+ }
+}
+
+void QSGCanvasItem::_doPainting(const QRectF& region)
+{
+ Q_D(QSGCanvasItem);
+ emit paint(QDeclarativeV8Handle::fromHandle(d->context->v8value())
+ , QSGContext2DTexture::tiledRect(region, d->tileSize));
+ if (d->texture)
+ d->texture->wake();
}
/*!
- Destroys the QSGCanvasItem.
+ \qmlproperty bool QtQuick2::Canvas::threadRendering
+ Holds the current canvas rendering mode.
+
+ When this property is true, all canvas painting commands
+ are rendered in a background rendering thread, otherwise
+ the rendering happens in the main GUI thread.
+
+ The default threadRendering value is true.
*/
-QSGCanvasItem::~QSGCanvasItem()
+void QSGCanvasItem::setThreadRendering(bool threadRendering)
{
+ Q_D(QSGCanvasItem);
+ if (d->threadRendering != threadRendering) {
+ d->threadRendering = threadRendering;
+
+ if (d->componentCompleted)
+ createTexture();
+
+ if (d->threadRendering)
+ connect(this, SIGNAL(painted()), SLOT(update()));
+ else
+ disconnect(this, SIGNAL(painted()), this, SLOT(update()));
+ emit threadRenderingChanged();
+ polish();
+ }
}
-void QSGCanvasItem::paint(QPainter *painter)
+void QSGCanvasItem::geometryChanged(const QRectF &newGeometry,
+ const QRectF &oldGeometry)
{
Q_D(QSGCanvasItem);
- if (d->context && d->context->isDirty()) {
- painter->setWindow(-d->canvasX, -d->canvasY, d->width, d->height);
- painter->setViewport(0, 0, d->width, d->height);
- painter->scale(d->contentsScale, d->contentsScale);
+ QSGItem::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();
+ }
- d->context->paint(painter);
- emit painted();
+ if (!d->hasCanvasWindow) {
+ d->canvasWindow = newGeometry;
+ emit canvasWindowChanged();
}
+
+ polish();
}
void QSGCanvasItem::componentComplete()
{
- const QMetaObject *metaObject = this->metaObject();
- int propertyCount = metaObject->propertyCount();
- int requestPaintMethod = metaObject->indexOfMethod("requestPaint(const QRect&)");
- for (int ii = QSGCanvasItem::staticMetaObject.propertyCount(); ii < propertyCount; ++ii) {
- QMetaProperty p = metaObject->property(ii);
- if (p.hasNotifySignal())
- QMetaObject::connect(this, p.notifySignalIndex(), this, requestPaintMethod, 0, 0);
- }
+ Q_D(QSGCanvasItem);
createContext();
+ createTexture();
+
+ markDirty(d->canvasWindow);
+ QSGItem::componentComplete();
+
+ d->baseUrl = qmlEngine(this)->contextForObject(this)->baseUrl();
+ d->componentCompleted = true;
+}
+
+void QSGCanvasItem::updatePolish()
+{
+ Q_D(QSGCanvasItem);
+
+ QSGItem::updatePolish();
+ if (d->texture) {
+ if (!d->threadRendering && 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 *QSGCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
+{
+ Q_D(QSGCanvasItem);
+ QSGContext2DNode *node = static_cast<QSGContext2DNode *>(oldNode);
+ if (!node)
+ node = new QSGContext2DNode(this);
+
+ node->setTexture(d->texture);
+ node->update();
+ return node;
+}
+
+void QSGCanvasItem::createTexture()
+{
+ Q_D(QSGCanvasItem);
- QSGPaintedItem::componentComplete();
+ if (!d->texture
+ || d->texture->threadRendering() != d->threadRendering
+ || d->texture->renderTarget() != d->renderTarget) {
+ if (d->texture) {
+ d->texture->deleteLater();
+ d->texture = 0;
+ }
+
+ if (d->renderTarget == QSGCanvasItem::Image) {
+ d->texture = new QSGContext2DImageTexture(d->threadRendering);
+ } else if (d->renderTarget == QSGCanvasItem::FramebufferObject) {
+ d->texture = new QSGContext2DFBOTexture();
+ }
+
+ if (d->threadRendering && !d->texture->supportThreadRendering()) {
+ qWarning("Canvas: render target does not support thread rendering, force to non-thread rendering mode.");
+ d->threadRendering = false;
+ emit threadRenderingChanged();
+ }
+
+ if (d->threadRendering)
+ connect(d->texture, SIGNAL(textureChanged()), this, SLOT(update()));
+
+ d->texture->setItem(this);
+ }
}
void QSGCanvasItem::createContext()
@@ -171,6 +413,13 @@ void QSGCanvasItem::createContext()
d->context->setV8Engine(e);
}
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Canvas::getContext(string contextId)
+
+ Currently, the canvas item only support 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 QSGCanvasItem::getContext(const QString &contextId)
{
Q_D(QSGCanvasItem);
@@ -181,91 +430,157 @@ QDeclarativeV8Handle QSGCanvasItem::getContext(const QString &contextId)
return QDeclarativeV8Handle::fromHandle(v8::Undefined());
}
-void QSGCanvasItem::requestPaint(const QRect& r)
+/*!
+ \qmlmethod void QtQuick2::Canvas::markDirty(rect region)
+
+ Mark the given \a region as dirty, so that when this region is visible
+ the canvas render will redraw it. During the rendering stage, the
+ canvas renderer may emit the canvas' "paint" signal so the actual painting
+ scripts can be putted into the canvas's "onPaint" function.
+
+ \sa QtQuick2::Canvas::paint
+ */
+void QSGCanvasItem::markDirty(const QRectF& region)
{
Q_D(QSGCanvasItem);
+ d->dirtyRect |= region;
+ polish();
+}
- QRect region;
- if (!r.isValid())
- region = QRect(d->canvasX, d->canvasY, d->width, d->height);
- else
- region = r;
- foreach (const QRect& rect, d->dirtyRegions) {
- if (rect.contains(region))
- return;
- }
+/*!
+ \qmlmethod bool QtQuick2::Canvas::save(string filename)
- d->unitedDirtyRegion = d->unitedDirtyRegion.unite(region);
- d->dirtyRegions.append(region);
- polish();
- update(d->unitedDirtyRegion);
-}
+ 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 the
+ current canvas visible window.
+ \sa canvasWindow canvasSize toDataURL
+ */
bool QSGCanvasItem::save(const QString &filename) const
{
- Q_D(const QSGCanvasItem);
- QSGPainterNode* node = static_cast<QSGPainterNode*>(d->paintNode);
- if (node) {
- QImage image = node->toImage();
- image.save(filename);
+ return toImage().save(filename);
+}
+
+QImage QSGCanvasItem::loadedImage(const QUrl& url)
+{
+ Q_D(QSGCanvasItem);
+ 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->pixmap().toImage();
+}
+void QSGCanvasItem::loadImage(const QUrl& url)
+{
+ Q_D(QSGCanvasItem);
+ 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()));
}
- return false;
}
-void QSGCanvasItem::updatePolish()
+void QSGCanvasItem::unloadImage(const QUrl& url)
{
Q_D(QSGCanvasItem);
- QDeclarativeV8Handle context = QDeclarativeV8Handle::fromHandle(d->context->v8value());
+ QUrl removeThis = d->baseUrl.resolved(url);
+ if (d->images.contains(removeThis)) {
+ delete d->images.value(removeThis);
+ d->images.remove(removeThis);
+ }
+}
+
+bool QSGCanvasItem::isImageError(const QUrl& url) const
+{
+ Q_D(const QSGCanvasItem);
+ QUrl fullPathUrl = d->baseUrl.resolved(url);
+ return d->images.contains(fullPathUrl)
+ && d->images.value(fullPathUrl)->isError();
+}
- d->context->setValid(true);
+bool QSGCanvasItem::isImageLoading(const QUrl& url) const
+{
+ Q_D(const QSGCanvasItem);
+ QUrl fullPathUrl = d->baseUrl.resolved(url);
+ return d->images.contains(fullPathUrl)
+ && d->images.value(fullPathUrl)->isLoading();
+}
- // d->context->setTileRect(QRectF(d->canvasX, d->canvasY, d->width, d->height).intersected(QRectF(0, 0, d->width, d->height)));
- // d->context->setTileRect(QRectF(d->canvasX, d->canvasY, d->width, d->height));
+bool QSGCanvasItem::isImageLoaded(const QUrl& url) const
+{
+ Q_D(const QSGCanvasItem);
+ QUrl fullPathUrl = d->baseUrl.resolved(url);
+ return d->images.contains(fullPathUrl)
+ && d->images.value(fullPathUrl)->isReady();
+}
- foreach (const QRect& region, d->dirtyRegions) {
- emit paint(context, region);
+QImage QSGCanvasItem::toImage(const QRectF& region) const
+{
+ Q_D(const QSGCanvasItem);
+ if (d->texture) {
+ if (region.isEmpty())
+ return d->texture->toImage(canvasWindow());
+ else
+ return d->texture->toImage(region);
}
- d->dirtyRegions.clear();
+ return QImage();
+}
- d->context->setValid(false);
+/*!
+ \qmlmethod string QtQuick2::Canvas::toDataURL(string mimeType)
- QSGPaintedItem::updatePolish();
-}
+ Returns a data: URL for the image in the canvas.
+ The default \a mimeType is "image/png".
+ Note: calling this method will force painting the whole canvas, not the
+ current canvas visible window.
+
+ \sa canvasWindow canvasSize save
+ */
QString QSGCanvasItem::toDataURL(const QString& mimeType) const
{
- Q_D(const QSGCanvasItem);
+ QImage image = toImage();
- QSGPainterNode* node = static_cast<QSGPainterNode*>(d->paintNode);
- if (node) {
- QImage image = node->toImage();
+ if (!image.isNull()) {
QByteArray ba;
QBuffer buffer(&ba);
buffer.open(QIODevice::WriteOnly);
QString mime = mimeType;
QString type;
- if (mimeType == QLatin1String("image/bmp"))
- type = QLatin1String("BMP");
- else if (mimeType == QLatin1String("image/jpeg"))
- type = QLatin1String("JPEG");
- else if (mimeType == QLatin1String("image/x-portable-pixmap"))
- type = QLatin1String("PPM");
- else if (mimeType == QLatin1String("image/tiff"))
- type = QLatin1String("TIFF");
- else if (mimeType == QLatin1String("image/xbm"))
- type = QLatin1String("XBM");
- else if (mimeType == QLatin1String("image/xpm"))
- type = QLatin1String("XPM");
+ if (mimeType == QLatin1Literal("image/bmp"))
+ type = QLatin1Literal("BMP");
+ else if (mimeType == QLatin1Literal("image/jpeg"))
+ type = QLatin1Literal("JPEG");
+ else if (mimeType == QLatin1Literal("image/x-portable-pixmap"))
+ type = QLatin1Literal("PPM");
+ else if (mimeType == QLatin1Literal("image/tiff"))
+ type = QLatin1Literal("TIFF");
+ else if (mimeType == QLatin1Literal("image/xbm"))
+ type = QLatin1Literal("XBM");
+ else if (mimeType == QLatin1Literal("image/xpm"))
+ type = QLatin1Literal("XPM");
else {
- type = QLatin1String("PNG");
- mime = QLatin1String("image/png");
+ type = QLatin1Literal("PNG");
+ mime = QLatin1Literal("image/png");
}
image.save(&buffer, type.toAscii());
buffer.close();
- QString dataUrl = QLatin1String("data:%1;base64,%2");
- return dataUrl.arg(mime).arg(ba.toBase64().constData());
+ QString dataUrl = QLatin1Literal("data:%1;base64,%2");
+ return dataUrl.arg(mime).arg(QLatin1String(ba.toBase64().constData()));
}
- return QLatin1String("data:,");
+ return QLatin1Literal("data:,");
}
QT_END_NAMESPACE
diff --git a/src/declarative/items/context2d/qsgcanvasitem_p.h b/src/declarative/items/context2d/qsgcanvasitem_p.h
index 9a6ccc27ed..565b894a7c 100644
--- a/src/declarative/items/context2d/qsgcanvasitem_p.h
+++ b/src/declarative/items/context2d/qsgcanvasitem_p.h
@@ -42,14 +42,10 @@
#ifndef QSGCANVASITEM_P_H
#define QSGCANVASITEM_P_H
-#include "qsgpainteditem.h"
+#include "qsgitem.h"
#include <private/qv8engine_p.h>
-#define QSGCANVASITEM_DEBUG //enable this for just DEBUG purpose!
-#ifdef QSGCANVASITEM_DEBUG
-#include <QElapsedTimer>
-#endif
QT_BEGIN_HEADER
@@ -58,41 +54,92 @@ QT_BEGIN_NAMESPACE
QT_MODULE(Declarative)
class QSGContext2D;
class QSGCanvasItemPrivate;
-class QSGCanvasItem : public QSGPaintedItem
+class QSGCanvasItem : public QSGItem
{
Q_OBJECT
- Q_PROPERTY(QPointF canvasPos READ canvasPos FINAL)
- Q_PROPERTY(qreal canvasX READ canvasX WRITE setCanvasX NOTIFY canvasXChanged FINAL)
- Q_PROPERTY(qreal canvasY READ canvasY WRITE setCanvasY NOTIFY canvasYChanged FINAL)
+ 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 threadRendering READ threadRendering WRITE setThreadRendering NOTIFY threadRenderingChanged)
+ Q_PROPERTY(RenderTarget renderTarget READ renderTarget WRITE setRenderTarget NOTIFY renderTargetChanged)
public:
+ enum RenderTarget {
+ Image,
+ FramebufferObject
+ };
+
+ enum ImageFilterMode {
+ Threshold,
+ GrayScale,
+ Brightness,
+ Invert,
+ Blur,
+ Opaque,
+ Convolute
+ };
+
QSGCanvasItem(QSGItem *parent = 0);
~QSGCanvasItem();
- void setCanvasX(qreal x);
- void setCanvasY(qreal y);
- qreal canvasX() const;
- qreal canvasY() const;
- QPointF canvasPos() const;
+
+ QSizeF canvasSize() const;
+ void setCanvasSize(const QSizeF &);
+
+ QSize tileSize() const;
+ void setTileSize(const QSize &);
+
+ QRectF canvasWindow() const;
+ void setCanvasWindow(const QRectF& rect);
+
+ bool threadRendering() const;
+ void setThreadRendering(bool theadRendering);
+
+ RenderTarget renderTarget() const;
+ void setRenderTarget(RenderTarget target);
+
+ QSGContext2D* context() const;
+ QImage toImage(const QRectF& region = QRectF()) const;
+
+ QImage loadedImage(const QUrl& url);
Q_SIGNALS:
- void painted();
void paint(QDeclarativeV8Handle context, const QRect &region);
- void canvasXChanged();
- void canvasYChanged();
+ void painted();
+ void canvasSizeChanged();
+ void tileSizeChanged();
+ void viewpointChanged();
+ void threadRenderingChanged();
+ 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 requestPaint(const QRect& region = QRect());
-
+ 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:
- void updatePolish();
- void paint(QPainter *painter);
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(QSGCanvasItem)
friend class QSGContext2D;
+ friend class QSGContext2DTexture;
};
QT_END_NAMESPACE
diff --git a/src/declarative/items/context2d/qsgcontext2d.cpp b/src/declarative/items/context2d/qsgcontext2d.cpp
index a2a57cb2ac..91bd5b5796 100644
--- a/src/declarative/items/context2d/qsgcontext2d.cpp
+++ b/src/declarative/items/context2d/qsgcontext2d.cpp
@@ -40,61 +40,43 @@
****************************************************************************/
#include "qsgcontext2d_p.h"
-#include "qsgcontext2d_p_p.h"
-#include "private/qsgadaptationlayer_p.h"
+#include "qsgcontext2dcommandbuffer_p.h"
#include "qsgcanvasitem_p.h"
-#include <QtOpenGL/qglframebufferobject.h>
+#include "qsgitem_p.h"
+#include "qsgshadereffectsource_p.h"
+
#include <QtCore/qdebug.h>
#include "private/qsgcontext_p.h"
#include "private/qdeclarativesvgparser_p.h"
+#include "private/qdeclarativepath_p.h"
+
+#include "private/qsgimage_p_p.h"
-#include <QtGui/qgraphicsitem.h>
-#include <QtGui/qapplication.h>
-#include <QtGui/qgraphicseffect.h>
#include <qdeclarativeinfo.h>
#include <QtCore/qmath.h>
-#include "qdeclarativepixmapcache_p.h"
-
-#include <private/qv8engine_p.h>
-#include "qvarlengtharray.h"
+#include "qv8engine_p.h"
+#include <QtOpenGL/QGLFramebufferObjectFormat>
+#include <QtOpenGL/QGLFramebufferObject>
+#include "qdeclarativeengine.h"
QT_BEGIN_NAMESPACE
-
+/*!
+ \qmlclass Context2D QSGContext2D
+ \inqmlmodule QtQuick 2
+ \since QtQuick 2.0
+ \brief The Context2D element allows you to draw 2d graphic shapes on Canvas item.
+*/
static const double Q_PI = 3.14159265358979323846; // pi
-template <class T>
-void memcpy_vector(QVector<T>* dst, const QVector<T>& src)
-{
- int pos = dst->size();
- dst->resize(pos + src.size());
- memmove(dst->data() + pos, src.constData(), sizeof(T) * src.size());
-}
-template <class T>
-void copy_vector(QVector<T>* dst, const QVector<T>& src)
-{
- int pos = dst->size();
- dst->resize(pos + src.size());
- for (int i = 0; i < src.size(); i++) {
- (*dst)[pos + i] = src[i];
- }
-}
-// Note, this is exported but in a private header as qtopengl depends on it.
-// But it really should be considered private API
-void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0);
-void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0);
#define DEGREES(t) ((t) * 180.0 / Q_PI)
#define qClamp(val, min, max) qMin(qMax(val, min), max)
-#define CHECK_CONTEXT(r) if (!r || !r->context) \
- V8THROW_ERROR("Not a Context2D object"); \
- if (!r->context->valid()) \
- V8THROW_ERROR("Context2D object is out of scope, please only access context2d APIs from onPaint method.");
+#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) \
- V8THROW_ERROR_SETTER("Not a Context2D object"); \
- if (!r->context->valid()) \
- V8THROW_ERROR_SETTER("Context2D object is out of scope, please only access context2d APIs from onPaint method.");
+#define CHECK_CONTEXT_SETTER(r) if (!r || !r->context || !r->context->buffer()) \
+ V8THROW_ERROR_SETTER("Not a Context2D object");
static inline int extractInt(const char **name)
{
@@ -274,7 +256,7 @@ static bool qt_get_hsl(const QString &string, QColor *color)
}
//### optimize further
-QColor colorFromString(const QString &name)
+QColor qt_color_from_string(const QString &name)
{
if (name.startsWith(QLatin1String("rgb"))) {
QRgb rgb;
@@ -289,8 +271,110 @@ QColor colorFromString(const QString &name)
return QColor(name);
}
+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;
+}
+
-static QPainter::CompositionMode compositeOperatorFromString(const QString &compositeOperator)
+
+class QSGContext2DEngineData : public QV8Engine::Deletable
+{
+public:
+ QSGContext2DEngineData(QV8Engine *engine);
+ ~QSGContext2DEngineData();
+
+ 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(QSGContext2DEngineData, engineData);
+
+
+
+class QV8Context2DResource : public QV8ObjectResource
+{
+ V8_RESOURCE_TYPE(Context2DType)
+public:
+ QV8Context2DResource(QV8Engine *e) : QV8ObjectResource(e) {}
+ QSGContext2D* context;
+};
+
+class QV8Context2DStyleResource : public QV8ObjectResource
+{
+ V8_RESOURCE_TYPE(Context2DStyleType)
+public:
+ QV8Context2DStyleResource(QV8Engine *e) : QV8ObjectResource(e) {}
+ QBrush brush;
+};
+
+class QV8Context2DPixelArrayResource : public QV8ObjectResource
+{
+ V8_RESOURCE_TYPE(Context2DPixelArrayType)
+public:
+ QV8Context2DPixelArrayResource(QV8Engine *e) : QV8ObjectResource(e) {}
+
+ QImage image;
+};
+
+static QImage qt_texture_to_image(QSGTexture* texture)
+{
+ if (!texture || !texture->textureId())
+ return QImage();
+ QGLFramebufferObjectFormat format;
+ format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+ format.setInternalTextureFormat(GL_RGBA);
+ format.setMipmap(false);
+ QGLFramebufferObject* fbo = new QGLFramebufferObject(texture->textureSize(), format);
+ fbo->drawTexture(QPointF(0,0), texture->textureId(), GL_TEXTURE_2D);
+ return fbo->toImage();
+}
+
+static QSGTexture* qt_item_to_texture(QSGItem* item)
+{
+ if (!item)
+ return 0;
+ QSGShaderEffectTexture* texture = new QSGShaderEffectTexture(item);
+ texture->setItem(QSGItemPrivate::get(item)->itemNode());
+ texture->setLive(true);
+
+ QRectF sourceRect = QRectF(0, 0, item->width(), item->height());
+
+ texture->setRect(sourceRect);
+ QSize textureSize = QSize(qCeil(qAbs(sourceRect.width())), qCeil(qAbs(sourceRect.height())));
+ texture->setSize(textureSize);
+ texture->setRecursive(false);
+ texture->setFormat(GL_RGBA);
+ texture->setHasMipmaps(false);
+ texture->markDirtyTexture();
+ texture->updateTexture();
+ return texture;
+}
+
+static QImage qt_item_to_image(QSGItem* item) {
+ return qt_texture_to_image(qt_item_to_texture(item));
+}
+
+static QPainter::CompositionMode qt_composite_mode_from_string(const QString &compositeOperator)
{
if (compositeOperator == QLatin1String("source-over")) {
return QPainter::CompositionMode_SourceOver;
@@ -308,20 +392,43 @@ static QPainter::CompositionMode compositeOperatorFromString(const QString &comp
return QPainter::CompositionMode_DestinationOut;
} else if (compositeOperator == QLatin1String("destination-over")) {
return QPainter::CompositionMode_DestinationOver;
- } else if (compositeOperator == QLatin1String("darker")) {
- return QPainter::CompositionMode_SourceOver;
} else if (compositeOperator == QLatin1String("lighter")) {
- return QPainter::CompositionMode_SourceOver;
+ return QPainter::CompositionMode_Plus;
} 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 compositeOperatorToString(QPainter::CompositionMode op)
+static QString qt_composite_mode_to_string(QPainter::CompositionMode op)
{
switch (op) {
case QPainter::CompositionMode_SourceOver:
@@ -329,11 +436,11 @@ static QString compositeOperatorToString(QPainter::CompositionMode op)
case QPainter::CompositionMode_DestinationOver:
return QLatin1String("destination-over");
case QPainter::CompositionMode_Clear:
- return QLatin1String("clear");
+ return QLatin1String("qt-clear");
case QPainter::CompositionMode_Source:
- return QLatin1String("source");
+ return QLatin1String("copy");
case QPainter::CompositionMode_Destination:
- return QLatin1String("destination");
+ return QLatin1String("qt-destination");
case QPainter::CompositionMode_SourceIn:
return QLatin1String("source-in");
case QPainter::CompositionMode_DestinationIn:
@@ -349,56 +456,61 @@ static QString compositeOperatorToString(QPainter::CompositionMode op)
case QPainter::CompositionMode_Xor:
return QLatin1String("xor");
case QPainter::CompositionMode_Plus:
- return QLatin1String("plus");
+ return QLatin1String("lighter");
case QPainter::CompositionMode_Multiply:
- return QLatin1String("multiply");
+ return QLatin1String("qt-multiply");
case QPainter::CompositionMode_Screen:
- return QLatin1String("screen");
+ return QLatin1String("qt-screen");
case QPainter::CompositionMode_Overlay:
- return QLatin1String("overlay");
+ return QLatin1String("qt-overlay");
case QPainter::CompositionMode_Darken:
- return QLatin1String("darken");
+ return QLatin1String("qt-darken");
case QPainter::CompositionMode_Lighten:
- return QLatin1String("lighten");
+ return QLatin1String("qt-lighten");
case QPainter::CompositionMode_ColorDodge:
- return QLatin1String("color-dodge");
+ return QLatin1String("qt-color-dodge");
case QPainter::CompositionMode_ColorBurn:
- return QLatin1String("color-burn");
+ return QLatin1String("qt-color-burn");
case QPainter::CompositionMode_HardLight:
- return QLatin1String("hard-light");
+ return QLatin1String("qt-hard-light");
case QPainter::CompositionMode_SoftLight:
- return QLatin1String("soft-light");
+ return QLatin1String("qt-soft-light");
case QPainter::CompositionMode_Difference:
- return QLatin1String("difference");
+ return QLatin1String("qt-difference");
case QPainter::CompositionMode_Exclusion:
- return QLatin1String("exclusion");
+ return QLatin1String("qt-exclusion");
default:
break;
}
return QString();
}
-class QV8Context2DResource : public QV8ObjectResource
-{
- V8_RESOURCE_TYPE(Context2DType)
-public:
- QV8Context2DResource(QV8Engine *e) : QV8ObjectResource(e) {}
- QDeclarativeGuard<QSGContext2D> context;
-};
-//static script functions
-static v8::Handle<v8::Value> ctx2d_sync(const v8::Arguments &args)
+static v8::Local<v8::Object> qt_create_image_data(qreal w, qreal h, QV8Engine* engine, const QImage& image)
{
- QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
- CHECK_CONTEXT(r)
-
-
- r->context->sync();
+ QSGContext2DEngineData *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(Qt::transparent);
+ } else {
+ Q_ASSERT(image.width() == w && image.height() == h);
+ r->image = image;
+ }
+ v8::Local<v8::Object> pixelData = ed->constructorPixelArray->NewInstance();
+ pixelData->SetExternalResource(r);
- return v8::Undefined();
+ imageData->SetInternalField(0, pixelData);
+ return imageData;
}
-// back-reference to the canvas, getter
+//static script functions
+
+/*!
+ \qmlproperty QtQuick2::Canvas QtQuick2::Context2D::canvas
+ Holds the canvas item that the context paints on.
+*/
static v8::Handle<v8::Value> ctx2d_canvas(v8::Local<v8::String>, const v8::AccessorInfo &info)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
@@ -410,65 +522,94 @@ static v8::Handle<v8::Value> ctx2d_canvas(v8::Local<v8::String>, const v8::Acces
return engine->newQObject(r->context->canvas());
}
-// state
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::restore()
+ Pops the top state on the stack, restoring the context to that state.
+*/
static v8::Handle<v8::Value> ctx2d_restore(const v8::Arguments &args)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
CHECK_CONTEXT(r)
-
- r->context->restore();
-
- return v8::Undefined();
+ r->context->popState();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D 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();
-
- return v8::Undefined();
+ r->context->reset();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::save()
+ Pushes the current state onto the stack.
+*/
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();
- r->context->save();
-
- return v8::Undefined();
+ return args.This();
}
// transformations
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::rotate(real angle)
+ Changes the transformation matrix to apply a rotation transformation with the given characteristics.
+ Note: The angle 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)
- r->context->rotate(args[0]->NumberValue());
+ if (args.Length() == 1) {
+ qreal angle = args[0]->NumberValue();
+ r->context->state.matrix.rotate(DEGREES(angle));
+ r->context->buffer()->updateMatrix(r->context->state.matrix);
+ }
- return v8::Undefined();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::scale(real x, real y)
+ Changes the transformation matrix to apply a scaling transformation with the given characteristics.
+*/
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)
- r->context->scale(args[0]->NumberValue(), args[1]->NumberValue());
+ if (args.Length() == 2) {
+ qreal x, y;
+ x = args[0]->NumberValue();
+ y = args[1]->NumberValue();
+ r->context->state.matrix.scale(x, y);
+ r->context->buffer()->updateMatrix(r->context->state.matrix);
+ }
- return v8::Undefined();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D 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.
+
+ \sa QtQuick2::Context2D::transform()
+*/
static v8::Handle<v8::Value> ctx2d_setTransform(const v8::Arguments &args)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
@@ -476,17 +617,24 @@ static v8::Handle<v8::Value> ctx2d_setTransform(const v8::Arguments &args)
if (args.Length() == 6) {
- r->context->setTransform(args[0]->NumberValue(),
- args[1]->NumberValue(),
- args[2]->NumberValue(),
- args[3]->NumberValue(),
- args[4]->NumberValue(),
- args[5]->NumberValue());
+ r->context->state.matrix = QTransform(args[0]->NumberValue(),
+ args[1]->NumberValue(),
+ args[2]->NumberValue(),
+ args[3]->NumberValue(),
+ args[4]->NumberValue(),
+ args[5]->NumberValue());
+ r->context->buffer()->updateMatrix(r->context->state.matrix);
}
- return v8::Undefined();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::transform(real a, real b, real c, real d, real e, real f)
+ Changes the transformation matrix to apply the matrix given by the arguments as described below.
+
+ \sa QtQuick2::Context2D::setTransform()
+*/
static v8::Handle<v8::Value> ctx2d_transform(const v8::Arguments &args)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
@@ -494,17 +642,22 @@ static v8::Handle<v8::Value> ctx2d_transform(const v8::Arguments &args)
if (args.Length() == 6) {
- r->context->transform(args[0]->NumberValue(),
- args[1]->NumberValue(),
- args[2]->NumberValue(),
- args[3]->NumberValue(),
- args[4]->NumberValue(),
- args[5]->NumberValue());
+ r->context->state.matrix *= QTransform(args[0]->NumberValue(),
+ args[1]->NumberValue(),
+ args[2]->NumberValue(),
+ args[3]->NumberValue(),
+ args[4]->NumberValue(),
+ args[5]->NumberValue());
+ r->context->buffer()->updateMatrix(r->context->state.matrix);
}
- return v8::Undefined();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::translate(real x, real y)
+ Changes the transformation matrix to apply a translation transformation with the given characteristics.
+*/
static v8::Handle<v8::Value> ctx2d_translate(const v8::Arguments &args)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
@@ -512,46 +665,99 @@ static v8::Handle<v8::Value> ctx2d_translate(const v8::Arguments &args)
if (args.Length() == 2) {
- r->context->translate(args[0]->NumberValue(),
- args[1]->NumberValue());
+ r->context->state.matrix.translate(args[0]->NumberValue(),
+ args[1]->NumberValue());
+ r->context->buffer()->updateMatrix(r->context->state.matrix);
}
- return v8::Undefined();
+ return args.This();
}
-// compositing
-// float getter/setter default 1.0
-static v8::Handle<v8::Value> ctx2d_globalAlpha(v8::Local<v8::String>, const v8::AccessorInfo &info)
+
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::resetTransform()
+ Reset the transformation matrix default value.
+
+ \sa QtQuick2::Context2D::transform(), QtQuick2::Context2D::setTransform()
+*/
+static v8::Handle<v8::Value> ctx2d_resetTransform(const v8::Arguments &args)
{
- QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
+ 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 v8::Number::New(r->context->globalAlpha());
+ return args.This();
}
-static void ctx2d_globalAlpha_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+
+/*!
+ \qmlmethod QtQuick2::Context2D 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>(info.This());
- CHECK_CONTEXT_SETTER(r)
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+ r->context->state.matrix.shear(args[0]->NumberValue(),
+ args[1]->NumberValue());
+ r->context->buffer()->updateMatrix(r->context->state.matrix);
- r->context->setGlobalAlpha(value->NumberValue());
+ return args.This();
}
+// compositing
-// read only property to indicate the validation of the context object
-static v8::Handle<v8::Value> ctx2d_valid(v8::Local<v8::String>, const v8::AccessorInfo &info)
+/*!
+ \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 (no additional transparency).
+*/
+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)
- QV8Engine *engine = V8ENGINE_ACCESSOR();
-
- return v8::Boolean::New(r->context->valid());
+ 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)
-// string getter/setter default "source-over"
+ qreal globalAlpha = value->NumberValue();
+
+ 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
+*/
static v8::Handle<v8::Value> ctx2d_globalCompositeOperation(v8::Local<v8::String>, const v8::AccessorInfo &info)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
@@ -560,7 +766,7 @@ static v8::Handle<v8::Value> ctx2d_globalCompositeOperation(v8::Local<v8::String
QV8Engine *engine = V8ENGINE_ACCESSOR();
- return engine->toString(r->context->globalCompositeOperation());
+ 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)
@@ -570,20 +776,29 @@ static void ctx2d_globalCompositeOperation_set(v8::Local<v8::String>, v8::Local<
QV8Engine *engine = V8ENGINE_ACCESSOR();
- r->context->setGlobalCompositeOperation(engine->toString(value));
+ QPainter::CompositionMode cm = qt_composite_mode_from_string(engine->toString(value));
+ if (cm != r->context->state.globalCompositeOperation) {
+ r->context->state.globalCompositeOperation = cm;
+ r->context->buffer()->setGlobalCompositeOperation(cm);
+ }
}
// colors and styles
-// getter/setter
+/*!
+ \qmlproperty variant QtQuick2::Context2D::fillStyle
+ Holds the current style used for filling shapes.
+ The style can be either a string containing a CSS color, or a CanvasGradient or CanvasPattern object. Invalid values are ignored.
+ \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();
-
- return engine->fromVariant(r->context->fillStyle());
+ return r->context->m_fillStyle;
}
static void ctx2d_fillStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
@@ -593,42 +808,71 @@ static void ctx2d_fillStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> valu
QV8Engine *engine = V8ENGINE_ACCESSOR();
- r->context->setFillStyle(engine->toVariant(value, -1));
-}
-
-// colors and styles
-// getter/setter
-static v8::Handle<v8::Value> ctx2d_fillColor(v8::Local<v8::String>, const v8::AccessorInfo &info)
+ r->context->m_fillStyle = value;
+ 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);
+ } 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);
+ }
+ }
+ } else if (value->IsString()) {
+ QColor color = qt_color_from_string(engine->toString(value));
+ if (color.isValid() && r->context->state.fillStyle != QBrush(color)) {
+ r->context->state.fillStyle = QBrush(color);
+ r->context->buffer()->setFillStyle(r->context->state.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->fillColor());
+ return engine->fromVariant(r->context->state.fillRule);
}
-static void ctx2d_fillColor_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+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();
- r->context->setFillColor(engine->toVariant(value, -1).value<QColor>());
-}
-
-//getter/setter
+ if ((value->IsString() && engine->toString(value) == "WindingFill")
+ ||(value->IsNumber() && value->NumberValue() == Qt::WindingFill)) {
+ r->context->state.fillRule = Qt::WindingFill;
+ } else if ((value->IsString() && engine->toString(value) == "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, or a CanvasGradient or CanvasPattern object. Invalid values are ignored.
+ \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();
-
- return engine->fromVariant(r->context->strokeStyle());
+ return r->context->m_strokeStyle;
}
static void ctx2d_strokeStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
@@ -638,31 +882,37 @@ static void ctx2d_strokeStyle_set(v8::Local<v8::String>, v8::Local<v8::Value> va
QV8Engine *engine = V8ENGINE_ACCESSOR();
- r->context->setStrokeStyle(engine->toVariant(value, -1));
-}
-
-// colors and styles
-// getter/setter
-v8::Handle<v8::Value> ctx2d_strokeColor(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->strokeColor());
+ r->context->m_strokeStyle = value;
+ 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);
+ } 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);
+ }
+ }
+ } else if (value->IsString()) {
+ QColor color = qt_color_from_string(engine->toString(value));
+ if (color.isValid() && r->context->state.strokeStyle != QBrush(color)) {
+ r->context->state.strokeStyle = QBrush(color);
+ r->context->buffer()->setStrokeStyle(r->context->state.strokeStyle);
+ }
+ }
}
-static void ctx2d_strokeColor_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->setStrokeColor(engine->toVariant(value, -1).value<QColor>());
-}
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::createLinearGradient(real x0, real y0, real x1, real y1)
+ Returns a CanvasGradient object that represents a linear gradient that paints along the line given by the coordinates
+ represented by the start point (\a x0, \a y0) and the end point (\a x1, \a y1).
+ \sa QtQuick2::Context2D::createRadialGradient
+ \sa QtQuick2::Context2D::createPattern
+ \sa QtQuick2::Context2D::fillStyle
+ \sa QtQuick2::Context2D::strokeStyle
+ */
static v8::Handle<v8::Value> ctx2d_createLinearGradient(const v8::Arguments &args)
{
@@ -673,16 +923,32 @@ static v8::Handle<v8::Value> ctx2d_createLinearGradient(const v8::Arguments &arg
QV8Engine *engine = V8ENGINE();
if (args.Length() == 4) {
- QObject* gradient = r->context->createLinearGradient(args[0]->NumberValue(),
- args[1]->NumberValue(),
- args[2]->NumberValue(),
- args[3]->NumberValue());
- return engine->newQObject(gradient);
+ //TODO:infinite or NaN, the method must raise a NOT_SUPPORTED_ERR
+ QSGContext2DEngineData *ed = engineData(engine);
+ v8::Local<v8::Object> gradient = ed->constructorGradient->NewInstance();
+ QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
+ r->brush = QLinearGradient(args[0]->NumberValue(),
+ args[1]->NumberValue(),
+ args[2]->NumberValue(),
+ args[3]->NumberValue());
+ gradient->SetExternalResource(r);
+ return gradient;
}
- return v8::Null();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D 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::createLinearGradient
+ \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());
@@ -692,26 +958,103 @@ static v8::Handle<v8::Value> ctx2d_createRadialGradient(const v8::Arguments &arg
QV8Engine *engine = V8ENGINE();
if (args.Length() == 6) {
- QObject* gradient = r->context->createRadialGradient(args[0]->NumberValue(),
- args[1]->NumberValue(),
- args[2]->NumberValue(),
- args[3]->NumberValue(),
- args[4]->NumberValue(),
- args[5]->NumberValue());
- return engine->newQObject(gradient);
+ QSGContext2DEngineData *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();
+ //TODO:infinite or NaN, a NOT_SUPPORTED_ERR exception must be raised.
+ //If either of r0 or r1 are negative, an INDEX_SIZE_ERR exception must be raised.
+ r->brush = QRadialGradient(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
+ gradient->SetExternalResource(r);
+ return gradient;
}
- return v8::Null();
+ return args.This();
}
+/*!
+ \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, 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)
{
- //TODO
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
+
+
+ QV8Engine *engine = V8ENGINE();
+
+ if (args.Length() == 2) {
+ QSGContext2DEngineData *ed = engineData(engine);
+ v8::Local<v8::Object> pattern = ed->constructorPattern->NewInstance();
+ QV8Context2DStyleResource *r = new QV8Context2DStyleResource(engine);
+
+ QImage img;
+
+ QSGItem* item = qobject_cast<QSGItem*>(engine->toQObject(args[0]));
+ if (item) {
+ img = qt_item_to_image(item);
+// if (img.isNull()) {
+// //exception: INVALID_STATE_ERR
+// }
+ } /*else {
+ //exception: TYPE_MISMATCH_ERR
+ }*/
+
+ QString repetition = engine->toString(args[1]);
+
+// if (repetition == "repeat" || repetition.isEmpty()) {
+// //TODO
+// } else if (repetition == "repeat-x") {
+// //TODO
+// } else if (repetition == "repeat-y") {
+// //TODO
+// } else if (repetition == "no-repeat") {
+// //TODO
+// } else {
+// //TODO: exception: SYNTAX_ERR
+// }
+ r->brush = img;
+ pattern->SetExternalResource(r);
+ return pattern;
+ }
return v8::Null();
}
// line styles
-// string getter/setter
+/*!
+ \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());
@@ -719,8 +1062,17 @@ v8::Handle<v8::Value> ctx2d_lineCap(v8::Local<v8::String>, const v8::AccessorInf
QV8Engine *engine = V8ENGINE_ACCESSOR();
-
- return engine->toString(r->context->lineCap());
+ 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)
@@ -730,10 +1082,35 @@ static void ctx2d_lineCap_set(v8::Local<v8::String>, v8::Local<v8::Value> value,
QV8Engine *engine = V8ENGINE_ACCESSOR();
- r->context->setLineCap(engine->toString(value));
-}
-
-// string getter/setter
+ 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;
+
+ 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());
@@ -741,8 +1118,17 @@ v8::Handle<v8::Value> ctx2d_lineJoin(v8::Local<v8::String>, const v8::AccessorIn
QV8Engine *engine = V8ENGINE_ACCESSOR();
-
- return engine->toString(r->context->lineJoin());
+ 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)
@@ -752,17 +1138,32 @@ static void ctx2d_lineJoin_set(v8::Local<v8::String>, v8::Local<v8::Value> value
QV8Engine *engine = V8ENGINE_ACCESSOR();
- r->context->setLineJoin(engine->toString(value));
+ 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;
+
+ if (join != r->context->state.lineJoin) {
+ r->context->state.lineJoin = join;
+ r->context->buffer()->setLineJoin(join);
+ }
}
-// float getter/setter
+/*!
+ \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->lineWidth());
+ 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)
@@ -770,17 +1171,26 @@ static void ctx2d_lineWidth_set(v8::Local<v8::String>, v8::Local<v8::Value> valu
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
CHECK_CONTEXT_SETTER(r)
- r->context->setLineWidth(value->NumberValue());
+ qreal w = value->NumberValue();
+
+ if (w > 0 && w != r->context->state.lineWidth) {
+ r->context->state.lineWidth = w;
+ r->context->buffer()->setLineWidth(w);
+ }
}
-// float getter/setter
+/*!
+ \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->miterLimit());
+ 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)
@@ -788,28 +1198,44 @@ static void ctx2d_miterLimit_set(v8::Local<v8::String>, v8::Local<v8::Value> val
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
CHECK_CONTEXT_SETTER(r)
- r->context->setMiterLimit(value->NumberValue());
+ qreal ml = value->NumberValue();
+
+ if (ml > 0 && ml != r->context->state.miterLimit) {
+ r->context->state.miterLimit = ml;
+ r->context->buffer()->setMiterLimit(ml);
+ }
}
// shadows
-// float getter/setter
+/*!
+ \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->shadowBlur());
+ 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();
- r->context->setShadowBlur(value->NumberValue());
+ if (blur > 0 && 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());
@@ -818,7 +1244,7 @@ v8::Handle<v8::Value> ctx2d_shadowColor(v8::Local<v8::String>, const v8::Accesso
QV8Engine *engine = V8ENGINE_ACCESSOR();
- return engine->toString(r->context->shadowColor());
+ 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)
@@ -828,16 +1254,28 @@ static void ctx2d_shadowColor_set(v8::Local<v8::String>, v8::Local<v8::Value> va
QV8Engine *engine = V8ENGINE_ACCESSOR();
- r->context->setShadowColor(engine->toString(value));
+ QColor color = qt_color_from_string(engine->toString(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->shadowOffsetX());
+ 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)
@@ -845,27 +1283,70 @@ static void ctx2d_shadowOffsetX_set(v8::Local<v8::String>, v8::Local<v8::Value>
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
CHECK_CONTEXT_SETTER(r)
- r->context->setShadowOffsetX(value->NumberValue());
+ //TODO: check value:infinite or NaN
+ qreal offsetX = value->NumberValue();
+ if (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->shadowOffsetY());
+ 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)
+ //TODO: check value:infinite or NaN
+ qreal offsetY = value->NumberValue();
+ if (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->setShadowOffsetY(value->NumberValue());
+ 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 QtQuick2::Context2D 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());
@@ -873,15 +1354,20 @@ static v8::Handle<v8::Value> ctx2d_clearRect(const v8::Arguments &args)
if (args.Length() == 4) {
- r->context->clearRect(args[0]->NumberValue(),
- args[1]->NumberValue(),
- args[2]->NumberValue(),
- args[3]->NumberValue());
+ r->context->buffer()->clearRect(args[0]->NumberValue(),
+ args[1]->NumberValue(),
+ args[2]->NumberValue(),
+ args[3]->NumberValue());
}
- return v8::Undefined();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D 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());
@@ -889,15 +1375,25 @@ static v8::Handle<v8::Value> ctx2d_fillRect(const v8::Arguments &args)
if (args.Length() == 4) {
- r->context->fillRect(args[0]->NumberValue(),
+ r->context->buffer()->fillRect(args[0]->NumberValue(),
args[1]->NumberValue(),
args[2]->NumberValue(),
args[3]->NumberValue());
}
- return v8::Undefined();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D 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());
@@ -905,34 +1401,53 @@ static v8::Handle<v8::Value> ctx2d_strokeRect(const v8::Arguments &args)
if (args.Length() == 4) {
- r->context->strokeRect(args[0]->NumberValue(),
- args[1]->NumberValue(),
- args[2]->NumberValue(),
- args[3]->NumberValue());
+ r->context->buffer()->strokeRect(args[0]->NumberValue(),
+ args[1]->NumberValue(),
+ args[2]->NumberValue(),
+ args[3]->NumberValue());
}
- return v8::Undefined();
+ return args.This();
}
// Complex shapes (paths) API
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::arc(real x, real y, real radius, real startAngle, real endAngle, bool anticlockwise)
+ Adds points to the subpath such that the arc described by the circumference of
+ the circle described by the arguments.
+
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-arc for details.
+ */
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) {
+ if (args.Length() == 6)
+ antiClockwise = args[5]->BooleanValue();
+
+ qreal radius = args[2]->NumberValue();
+ //Throws an INDEX_SIZE_ERR exception if the given radius is negative.
r->context->arc(args[0]->NumberValue(),
args[1]->NumberValue(),
- args[2]->NumberValue(),
+ radius,
args[3]->NumberValue(),
args[4]->NumberValue(),
- args[5]->NumberValue());
+ antiClockwise);
}
- return v8::Undefined();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D 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.
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-arcto for details.
+ */
static v8::Handle<v8::Value> ctx2d_arcTo(const v8::Arguments &args)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
@@ -947,9 +1462,14 @@ static v8::Handle<v8::Value> ctx2d_arcTo(const v8::Arguments &args)
args[4]->NumberValue());
}
- return v8::Undefined();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::beginPath()
+
+ Resets the current path.
+ */
static v8::Handle<v8::Value> ctx2d_beginPath(const v8::Arguments &args)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
@@ -958,16 +1478,23 @@ static v8::Handle<v8::Value> ctx2d_beginPath(const v8::Arguments &args)
r->context->beginPath();
- return v8::Undefined();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::bezierCurveTo(real cp1x, real cp1y, real cp2x, real cp2y, real x, real y)
+
+ Adds the given point to the current subpath, connected to the previous one by a cubic Bézier curve with the given control points.
+
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-beziercurveto for details.
+ */
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() == 5) {
+ if (args.Length() == 6) {
r->context->bezierCurveTo(args[0]->NumberValue(),
args[1]->NumberValue(),
args[2]->NumberValue(),
@@ -976,20 +1503,34 @@ static v8::Handle<v8::Value> ctx2d_bezierCurveTo(const v8::Arguments &args)
args[5]->NumberValue());
}
- return v8::Undefined();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::clip()
+
+ constrains the clipping region to the given path.
+
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-clip for details.
+ */
static v8::Handle<v8::Value> ctx2d_clip(const v8::Arguments &args)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
CHECK_CONTEXT(r)
-
- r->context->clip();
+ r->context->state.clipPath = r->context->m_path;
+ r->context->buffer()->clip(r->context->state.clipPath);
- return v8::Undefined();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::closePath()
+
+ Marks the current subpath as closed, and starts a new subpath with a point the same as the start and end of the newly closed subpath.
+
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-closepath for details.
+ */
static v8::Handle<v8::Value> ctx2d_closePath(const v8::Arguments &args)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
@@ -998,20 +1539,33 @@ static v8::Handle<v8::Value> ctx2d_closePath(const v8::Arguments &args)
r->context->closePath();
- return v8::Undefined();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::fill()
+
+ Fills the subpaths with the current fill style.
+
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-fill for details.
+
+ \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);
- r->context->fill();
-
- return v8::Undefined();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::lineTo(real x, real y)
+
+ Adds the given point to the current subpath, connected to the previous one by a straight line.
+ */
static v8::Handle<v8::Value> ctx2d_lineTo(const v8::Arguments &args)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
@@ -1023,9 +1577,14 @@ static v8::Handle<v8::Value> ctx2d_lineTo(const v8::Arguments &args)
args[1]->NumberValue());
}
- return v8::Undefined();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D 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());
@@ -1037,9 +1596,16 @@ static v8::Handle<v8::Value> ctx2d_moveTo(const v8::Arguments &args)
args[1]->NumberValue());
}
- return v8::Undefined();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::quadraticCurveTo(real cpx, real cpy, real x, real y)
+
+ Adds the given point to the current subpath, connected to the previous one by a quadratic Bézier curve with the given control point.
+
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-quadraticcurveto for details.
+ */
static v8::Handle<v8::Value> ctx2d_quadraticCurveTo(const v8::Arguments &args)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
@@ -1053,9 +1619,16 @@ static v8::Handle<v8::Value> ctx2d_quadraticCurveTo(const v8::Arguments &args)
args[3]->NumberValue());
}
- return v8::Undefined();
+ return args.This();
}
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::rect(real x, real y, real w, real h)
+
+ Adds a new closed subpath to the path, representing the given rectangle.
+
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-rect for details.
+ */
static v8::Handle<v8::Value> ctx2d_rect(const v8::Arguments &args)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
@@ -1069,85 +1642,145 @@ static v8::Handle<v8::Value> ctx2d_rect(const v8::Arguments &args)
args[3]->NumberValue());
}
- return v8::Undefined();
+ return args.This();
}
-static v8::Handle<v8::Value> ctx2d_stroke(const v8::Arguments &args)
+/*!
+ \qmlmethod QtQuick2::Context2D 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 xRadius and yRadius arguments specify the radii 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)
- r->context->stroke();
+ if (args.Length() == 6) {
+ r->context->roundedRect(args[0]->NumberValue(),
+ args[1]->NumberValue(),
+ args[2]->NumberValue(),
+ args[3]->NumberValue(),
+ args[4]->NumberValue(),
+ args[5]->NumberValue());
+ }
- return v8::Undefined();
+ return args.This();
}
-static v8::Handle<v8::Value> ctx2d_isPointInPath(const v8::Arguments &args)
+/*!
+ \qmlmethod QtQuick2::Context2D 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)
- bool pointInPath = false;
- if (args.Length() == 2) {
- pointInPath = r->context->isPointInPath(args[0]->NumberValue(),
- args[1]->NumberValue());
+ if (args.Length() == 6) {
+ r->context->ellipse(args[0]->NumberValue(),
+ args[1]->NumberValue(),
+ args[2]->NumberValue(),
+ args[3]->NumberValue());
}
- return v8::Boolean::New(pointInPath);
+ return args.This();
}
-static v8::Handle<v8::Value> ctx2d_setPathString(const v8::Arguments &args)
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::text(string text, real x, real y)
+
+ Adds the given \a 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 (x, 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() == 1) {
- r->context->setPathString(engine->toString(args[0]));
+ if (args.Length() == 3) {
+ r->context->text(engine->toString(args[0]),
+ args[1]->NumberValue(),
+ args[2]->NumberValue());
}
- return v8::Undefined();
+
+ return args.This();
}
-static v8::Handle<v8::Value> ctx2d_path(v8::Local<v8::String>, const v8::AccessorInfo &info)
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::stroke()
+
+ Strokes the subpaths with the current stroke style.
+
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-stroke for details.
+
+ \sa QtQuick2::Context2D::strokeStyle
+ */
+static v8::Handle<v8::Value> ctx2d_stroke(const v8::Arguments &args)
{
-// QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
-// CHECK_CONTEXT(r)
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
-// QV8Engine *engine = V8ENGINE();
- return v8::Undefined();
+ r->context->buffer()->stroke(r->context->m_path);
+
+ return args.This();
}
-static v8::Handle<v8::Value> ctx2d_path_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::isPointInPath(real x, real y)
+
+ Returns true if the given point is in the current path.
+
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-ispointinpath for details.
+ */
+static v8::Handle<v8::Value> ctx2d_isPointInPath(const v8::Arguments &args)
{
-// QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
-// CHECK_CONTEXT(r)
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
-// QV8Engine *engine = V8ENGINE();
-// if (args.Length() == 1) {
-// r->context->setPathString(engine->toString(args[0]));
-// }
- return v8::Undefined();
+ bool pointInPath = false;
+ if (args.Length() == 2) {
+ pointInPath = r->context->isPointInPath(args[0]->NumberValue(),
+ args[1]->NumberValue());
+ }
+
+ return v8::Boolean::New(pointInPath);
}
-static v8::Handle<v8::Value> ctx2d_createPath(const v8::Arguments &args)
+static v8::Handle<v8::Value> ctx2d_drawFocusRing(const v8::Arguments &args)
{
-// QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
-// CHECK_CONTEXT(r)
-
+ V8THROW_ERROR("Context2D::drawFocusRing is not supported")
+ return args.This();
+}
-// QV8Engine *engine = V8ENGINE();
-// if (args.Length() == 1) {
-// r->context->setPathString(engine->toString(args[0]));
-// }
- return v8::Undefined();
+static v8::Handle<v8::Value> ctx2d_setCaretSelectionRect(const v8::Arguments &args)
+{
+ V8THROW_ERROR("Context2D::setCaretSelectionRect is not supported")
+ return args.This();
}
+static v8::Handle<v8::Value> ctx2d_caretBlinkRate(const v8::Arguments &args)
+{
+ V8THROW_ERROR("Context2D::caretBlinkRate is not supported")
+
+ return args.This();
+}
// text
+/*!
+ \qmlproperty string QtQuick2::Context2D::font
+ Holds the current font settings, default value is "10px sans-serif".
+
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-font for details.
+ */
v8::Handle<v8::Value> ctx2d_font(v8::Local<v8::String>, const v8::AccessorInfo &info)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
@@ -1155,7 +1788,7 @@ v8::Handle<v8::Value> ctx2d_font(v8::Local<v8::String>, const v8::AccessorInfo &
QV8Engine *engine = V8ENGINE_ACCESSOR();
- return engine->toString(r->context->font());
+ return engine->toString(r->context->m_fontString);
}
static void ctx2d_font_set(v8::Local<v8::String>, v8::Local<v8::Value> value, const v8::AccessorInfo &info)
@@ -1164,17 +1797,48 @@ static void ctx2d_font_set(v8::Local<v8::String>, v8::Local<v8::Value> value, co
CHECK_CONTEXT_SETTER(r)
QV8Engine *engine = V8ENGINE_ACCESSOR();
-
- r->context->setFont(engine->toString(value));
-}
-
+ QString fs = engine->toString(value);
+ if (fs != r->context->m_fontString) {
+ r->context->m_fontString = fs;
+ QFont font = qt_font_from_string(fs);
+ 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 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();
-
- return engine->toString(r->context->textAlign());
+ switch (r->context->state.textAlign) {
+ case QSGContext2D::Start:
+ return engine->toString(QLatin1String("start"));
+ case QSGContext2D::End:
+ return engine->toString(QLatin1String("end"));
+ case QSGContext2D::Left:
+ return engine->toString(QLatin1String("left"));
+ case QSGContext2D::Right:
+ return engine->toString(QLatin1String("right"));
+ case QSGContext2D::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)
@@ -1183,17 +1847,62 @@ static void ctx2d_textAlign_set(v8::Local<v8::String>, v8::Local<v8::Value> valu
CHECK_CONTEXT_SETTER(r)
QV8Engine *engine = V8ENGINE_ACCESSOR();
- r->context->setTextAlign(engine->toString(value));
+ QString textAlign = engine->toString(value);
+
+ QSGContext2D::TextAlignType ta;
+ if (textAlign == QLatin1String("start"))
+ ta = QSGContext2D::Start;
+ else if (textAlign == QLatin1String("end"))
+ ta = QSGContext2D::End;
+ else if (textAlign == QLatin1String("left"))
+ ta = QSGContext2D::Left;
+ else if (textAlign == QLatin1String("right"))
+ ta = QSGContext2D::Right;
+ else if (textAlign == QLatin1String("center"))
+ ta = QSGContext2D::Center;
+
+ 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".
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-textbaseline for details.
+ */
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();
-
- return engine->toString(r->context->textBaseline());
+ switch (r->context->state.textBaseline) {
+ case QSGContext2D::Alphabetic:
+ return engine->toString(QLatin1String("alphabetic"));
+ case QSGContext2D::Hanging:
+ return engine->toString(QLatin1String("hanging"));
+ case QSGContext2D::Top:
+ return engine->toString(QLatin1String("top"));
+ case QSGContext2D::Bottom:
+ return engine->toString(QLatin1String("bottom"));
+ case QSGContext2D::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)
@@ -1201,10 +1910,30 @@ static void ctx2d_textBaseline_set(v8::Local<v8::String>, v8::Local<v8::Value> v
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(info.This());
CHECK_CONTEXT_SETTER(r)
QV8Engine *engine = V8ENGINE_ACCESSOR();
-
- r->context->setTextBaseline(engine->toString(value));
-}
-
+ QString textBaseline = engine->toString(value);
+
+ QSGContext2D::TextBaseLineType tb;
+ if (textBaseline == QLatin1String("alphabetic"))
+ tb = QSGContext2D::Alphabetic;
+ else if (textBaseline == QLatin1String("hanging"))
+ tb = QSGContext2D::Hanging;
+ else if (textBaseline == QLatin1String("top"))
+ tb = QSGContext2D::Top;
+ else if (textBaseline == QLatin1String("bottom"))
+ tb = QSGContext2D::Bottom;
+ else if (textBaseline == QLatin1String("middle"))
+ tb = QSGContext2D::Middle;
+
+ if (tb != r->context->state.textBaseline) {
+ r->context->state.textBaseline = tb;
+ }
+}
+
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::fillText(text, x, y)
+ Fills the given text at the given position.
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-filltext for details.
+ */
static v8::Handle<v8::Value> ctx2d_fillText(const v8::Arguments &args)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
@@ -1214,14 +1943,19 @@ static v8::Handle<v8::Value> ctx2d_fillText(const v8::Arguments &args)
QV8Engine *engine = V8ENGINE();
if (args.Length() == 3) {
- r->context->fillText(engine->toString(args[0]),
- args[1]->NumberValue(),
- args[2]->NumberValue());
+ QPainterPath textPath = r->context->createTextGlyphs(args[1]->NumberValue(),
+ args[2]->NumberValue(),
+ engine->toString(args[0]));
+ r->context->buffer()->fill(textPath);
}
- return v8::Undefined();
+ return args.This();
}
-
+/*!
+ \qmlmethod QtQuick2::Context2D QtQuick2::Context2D::strokeText(text, x, y)
+ Strokes the given text at the given position.
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-stroketext for details.
+ */
static v8::Handle<v8::Value> ctx2d_strokeText(const v8::Arguments &args)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
@@ -1231,16 +1965,39 @@ static v8::Handle<v8::Value> ctx2d_strokeText(const v8::Arguments &args)
QV8Engine *engine = V8ENGINE();
if (args.Length() == 3) {
- r->context->strokeText(engine->toString(args[0]),
- args[1]->NumberValue(),
- args[2]->NumberValue());
- }
-
- return v8::Undefined();
-}
-
-// drawing images
-static v8::Handle<v8::Value> ctx2d_drawImage(const v8::Arguments &args)
+ QPainterPath textPath = r->context->createTextGlyphs(args[1]->NumberValue(),
+ args[2]->NumberValue(),
+ 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 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.
+ See http://www.w3.org/TR/2dcontext/#dom-textmetrics-width for more details.
+ */
+
+/*!
+ \qmlmethod variant QtQuick2::Context2D::measureText(text)
+ Returns a TextMetrics object with the metrics of the given text in the current font.
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-measuretext for details.
+ */
+static v8::Handle<v8::Value> ctx2d_measureText(const v8::Arguments &args)
{
QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
CHECK_CONTEXT(r)
@@ -1248,1404 +2005,866 @@ static v8::Handle<v8::Value> ctx2d_drawImage(const v8::Arguments &args)
QV8Engine *engine = V8ENGINE();
- if (args.Length() == 3) {
- r->context->drawImage(engine->toString(args[0]),
- args[1]->NumberValue(),
- args[2]->NumberValue());
- } else if (args.Length() == 5) {
- r->context->drawImage(engine->toString(args[0]),
- args[1]->NumberValue(),
- args[2]->NumberValue(),
- args[3]->NumberValue(),
- args[4]->NumberValue());
- } else if (args.Length() == 9) {
- r->context->drawImage(engine->toString(args[0]),
- args[1]->NumberValue(),
- args[2]->NumberValue(),
- args[3]->NumberValue(),
- args[4]->NumberValue(),
- args[5]->NumberValue(),
- args[6]->NumberValue(),
- args[7]->NumberValue(),
- args[8]->NumberValue());
+ 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();
}
-// pixel manipulation
-static v8::Handle<v8::Value> ctx2d_createImageData(const v8::Arguments &args)
-{
- //#TODO
- return v8::Undefined();
-}
-
-static v8::Handle<v8::Value> ctx2d_getImageData(const v8::Arguments &args)
-{
- //#TODO
- return v8::Undefined();
-}
-
-static v8::Handle<v8::Value> ctx2d_putImageData(const v8::Arguments &args)
+// 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 or a image url. When given as Image
+ type, if the image isn't fully loaded, will draw nothing. When given as url string,
+ the context loads the image asynchorously and redraw the canvas when the image is loaded.
+
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage for more details.
+ */
+/*!
+ \qmlmethod QtQuick2::Context2D::drawImage(variant image, real dx, real dy, real dw, real dh)
+ Draws the given item as \a image onto the canvas at point (\a dx, \a dy) and with width \a dw,
+ height \a dh.
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage for more details.
+ */
+/*!
+ \qmlmethod QtQuick2::Context2D::drawImage(variant image, real sx, real sy, real sw, sh, real dx, real dy, real dw, dh)
+ 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.
+ See http://www.w3.org/TR/2dcontext/#dom-context-2d-drawimage for more details.
+ */
+static v8::Handle<v8::Value> ctx2d_drawImage(const v8::Arguments &args)
{
- //#TODO
- return v8::Undefined();
-}
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
-bool QSGContext2DPrivate::hasShadow() const
-{
- return state.shadowColor.isValid()
- && state.shadowColor.alpha()
- && (state.shadowBlur || state.shadowOffsetX || state.shadowOffsetY);
-}
-void QSGContext2DPrivate::clearShadow()
-{
- state.shadowOffsetX = 0;
- state.shadowOffsetY = 0;
- state.shadowBlur = 0;
- state.shadowColor = QColor();
-}
+ QV8Engine *engine = V8ENGINE();
-QImage QSGContext2DPrivate::makeShadowImage(const QPixmap& pix)
-{
- QImage shadowImg(pix.width() + state.shadowBlur * 2 + qAbs(state.shadowOffsetX),
- pix.height() + state.shadowBlur *2 + qAbs(state.shadowOffsetY),
- QImage::Format_ARGB32);
- shadowImg.fill(0);
- QPainter tmpPainter(&shadowImg);
- tmpPainter.setCompositionMode(QPainter::CompositionMode_Source);
- qreal shadowX = state.shadowOffsetX > 0? state.shadowOffsetX : 0;
- qreal shadowY = state.shadowOffsetY > 0? state.shadowOffsetY : 0;
+ //TODO: handle exceptions
- tmpPainter.drawPixmap(shadowX, shadowY, pix);
- tmpPainter.end();
+ qreal sx, sy, sw, sh, dx, dy, dw, dh;
- // blur the alpha channel
- if (state.shadowBlur > 0) {
- QImage blurred(shadowImg.size(), QImage::Format_ARGB32);
- blurred.fill(0);
- QPainter blurPainter(&blurred);
- qt_blurImage(&blurPainter, shadowImg, state.shadowBlur, false, true);
- blurPainter.end();
- shadowImg = blurred;
+ if (args.Length() != 3 && args.Length() != 5 && args.Length() != 9) {
+ //parameter error
+ return args.This();
}
- // blacken the image with shadow color...
- tmpPainter.begin(&shadowImg);
- tmpPainter.setCompositionMode(QPainter::CompositionMode_SourceIn);
- tmpPainter.fillRect(shadowImg.rect(), state.shadowColor);
- tmpPainter.end();
- return shadowImg;
-}
-
-void QSGContext2DPrivate::fillRectShadow(QPainter* p, QRectF shadowRect)
-{
- QRectF r = shadowRect;
- r.moveTo(0, 0);
- QImage shadowImage(r.size().width() + 1, r.size().height() + 1, QImage::Format_ARGB32);
- QPainter tp;
- tp.begin(&shadowImage);
- tp.fillRect(r, p->brush());
- tp.end();
- shadowImage = makeShadowImage(QPixmap::fromImage(shadowImage));
+ QImage image;
+ if (args[0]->IsString()) {
+ image = r->context->createImage(QUrl(engine->toString(args[0]->ToString())));
+ } /*else if (args[0]->IsObject()) {
+ QSGImage* imageItem = qobject_cast<QSGImage*>(engine->toQObject(args[0]->ToObject()));
+ if (imageItem) {
+ image = imageItem->pixmap().toImage();
+ } else {
+ //wrong image type
+ return args.This();
+ }
+ }*/
+ if (args.Length() == 3) {
+ dx = args[1]->NumberValue();
+ dy = args[2]->NumberValue();
+ sx = 0;
+ sy = 0;
+ sw = image.isNull()? -1 : image.width();
+ sh = image.isNull()? -1 : image.height();
+ dw = sw;
+ dh = sh;
+ } else if (args.Length() == 5) {
+ sx = 0;
+ sy = 0;
+ sw = image.isNull()? -1 : image.width();
+ sh = image.isNull()? -1 : 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 {
+ //error
+ return args.This();
+ }
- qreal dx = shadowRect.left() + (state.shadowOffsetX < 0? state.shadowOffsetX:0);
- qreal dy = shadowRect.top() + (state.shadowOffsetY < 0? state.shadowOffsetY:0);
+ r->context->buffer()->drawImage(image,sx, sy, sw, sh, dx, dy, dw, dh);
- p->drawImage(dx, dy, shadowImage);
- p->fillRect(shadowRect, p->brush());
+ return args.This();
}
-void QSGContext2DPrivate::fillShadowPath(QPainter* p, const QPainterPath& path)
-{
- QRectF r = path.boundingRect();
- QImage img(r.size().width() + r.left() + 1,
- r.size().height() + r.top() + 1,
- QImage::Format_ARGB32);
- img.fill(0);
- QPainter tp(&img);
- tp.fillPath(path.translated(0, 0), p->brush());
- tp.end();
-
- QImage shadowImage = makeShadowImage(QPixmap::fromImage(img));
- qreal dx = r.left() + (state.shadowOffsetX < 0? state.shadowOffsetX:0);
- qreal dy = r.top() + (state.shadowOffsetY < 0? state.shadowOffsetY:0);
-
- p->drawImage(dx, dy, shadowImage);
- p->fillPath(path, p->brush());
-}
+// pixel manipulation
+/*!
+ \qmlclass QtQuick2::CanvasImageData
-void QSGContext2DPrivate::strokeShadowPath(QPainter* p, const QPainterPath& path)
+ */
+/*!
+ \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)
{
- QRectF r = path.boundingRect();
- QImage img(r.size().width() + r.left() + 1,
- r.size().height() + r.top() + 1,
- QImage::Format_ARGB32);
- img.fill(0);
- QPainter tp(&img);
- tp.strokePath(path, p->pen());
- tp.end();
-
- QImage shadowImage = makeShadowImage(QPixmap::fromImage(img));
- qreal dx = r.left() + (state.shadowOffsetX < 0? state.shadowOffsetX:0);
- qreal dy = r.top() + (state.shadowOffsetY < 0? state.shadowOffsetY:0);
- p->drawImage(dx, dy, shadowImage);
- p->strokePath(path, p->pen());
+ 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());
}
-void QSGContext2DPrivate::clear()
+/*!
+ \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)
{
- clearRect(0, 0, size.width(), size.height());
+ 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());
}
-void QSGContext2DPrivate::reset()
+/*!
+ \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)
{
- stateStack.clear();
- state.matrix = QMatrix();
- state.clipPath = QPainterPath();
- state.strokeStyle = Qt::black;
- state.fillStyle = Qt::black;
- state.globalAlpha = 1.0;
- state.lineWidth = 1;
- state.lineCap = Qt::FlatCap;
- state.lineJoin = Qt::MiterJoin;
- state.miterLimit = 10;
- state.shadowOffsetX = 0;
- state.shadowOffsetY = 0;
- state.shadowBlur = 0;
- state.shadowColor = qRgba(0, 0, 0, 0);
- state.globalCompositeOperation = QPainter::CompositionMode_SourceOver;
- state.font = QFont();
- state.textAlign = QSGContext2D::Start;
- state.textBaseline = QSGContext2D::Alphabetic;
- clear();
+ return args.This()->GetInternalField(0);
}
-
-void QSGContext2DPrivate::updateMatrix(const QMatrix& m)
+static v8::Handle<v8::Value> ctx2d_imageData_mirror(const v8::Arguments &args)
{
- commands.push_back(QSGContext2D::UpdateMatrix);
- matrixes.push_back(m);
-}
+ 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();
+ }
-void QSGContext2DPrivate::save()
-{
- stateStack.push(state);
+ r->image = r->image.mirrored(horizontal, vertical);
+ return args.This();
}
-void QSGContext2DPrivate::restore()
+
+static v8::Handle<v8::Value> ctx2d_imageData_filter(const v8::Arguments &args)
{
- if (!stateStack.isEmpty()) {
- bool update = false;
- QSGContext2D::State s = stateStack.pop();
- if (state.matrix != s.matrix) {
- updateMatrix(s.matrix);
- update = true;
- }
+ QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This()->GetInternalField(0)->ToObject());
- if (s.pen != state.pen) {
- commands.push_back(QSGContext2D::UpdatePen);
- pens.push_back(s.pen);
- update = true;
- }
+ if (!r) {
+ //error
+ return v8::Undefined();
+ }
- if (s.globalAlpha != state.globalAlpha) {
- commands.push_back(QSGContext2D::GlobalAlpha);
- reals.push_back(s.globalAlpha);
- update = true;
+ if (args.Length() >= 1) {
+ int filterFlag = args[0]->IntegerValue();
+ switch(filterFlag) {
+ case QSGCanvasItem::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]);
+ }
+ }
}
-
- if (s.globalCompositeOperation != state.globalCompositeOperation) {
- commands.push_back(QSGContext2D::GlobalCompositeOperation);
- ints.push_back(s.globalCompositeOperation);
- update = true;
+ break;
+ case QSGCanvasItem::Threshold :
+ {
+ int threshold = 127;
+ if (args.Length() > 1)
+ threshold = 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* rgb = ((unsigned char*)&row[x]);
+ unsigned char v = qGray(rgb[0], rgb[1], rgb[2]) >= threshold ? 255 : 0;
+ rgb[0] = rgb[1] = rgb[2] = v;
+ }
+ }
}
-
- if (s.font != state.font) {
- commands.push_back(QSGContext2D::Font);
- fonts.push_back(s.font);
- update = true;
+ break;
+ case QSGCanvasItem::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;
+ }
+ }
}
-
- if (s.fillStyle != state.fillStyle) {
- commands.push_back(QSGContext2D::FillStyle);
- brushes.push_back(s.fillStyle);
- update = true;
+ break;
+ case QSGCanvasItem::Invert :
+ {
+ r->image.invertPixels();
}
-
- if (s.clipPath != state.clipPath) {
- //commands.push_back(QSGContext2D::ClipPath);
- update = true;
+ break;
+ case QSGCanvasItem::Blur :
+ {
+ QImage blurred(r->image.size(), QImage::Format_ARGB32);
+ qreal blur = 10;
+ if (args.Length() > 1)
+ blur = args[1]->NumberValue();
+
+ blurred.fill(Qt::transparent);
+ QPainter blurPainter(&blurred);
+ qt_blurImage(&blurPainter, r->image, blur, true, false);
+ blurPainter.end();
+ r->image = blurred;
}
-
- if (s.textAlign != state.textAlign) {
- commands.push_back(QSGContext2D::TextAlign);
- update = true;
+ break;
+ case QSGCanvasItem::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;
+ }
+ }
}
-
- if (s.textBaseline != state.textBaseline) {
- commands.push_back(QSGContext2D::TextBaseline);
- update = true;
+ break;
+ case QSGCanvasItem::Convolute :
+ {
+ if (args.Length() > 1 && args[1]->IsArray()) {
+ v8::Local<v8::Array> array = v8::Local<v8::Array>::Cast(args[1]);
+ QVector<int> weights;
+ for (uint32_t i = 0; i < array->Length(); ++i)
+ weights.append(array->Get(i)->NumberValue());
+ int sides = qRound(qSqrt(weights.size()));
+ int half = qFloor(sides/2);
+
+ QImage image = QImage(r->image.size(), QImage::Format_ARGB32);
+ int w = r->image.width();
+ int h = r->image.height();
+ for (int y = 0; y < image.height(); ++y) {
+ QRgb *dRow = (QRgb*)image.scanLine(y);
+ for (int x = 0; x < image.width(); ++x) {
+ unsigned char* dRgb = ((unsigned char*)&dRow[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) {
+ QRgb *sRow = (QRgb*)(r->image.scanLine(scy));
+ unsigned char* sRgb = ((unsigned char*)&sRow[scx]);
+ int wt = 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;
+ }
+ }
+ r->image = image;
+ } else {
+ //error
+ }
}
-
- if (s.shadowBlur != state.shadowBlur
- || s.shadowColor != state.shadowColor
- || s.shadowOffsetX != state.shadowOffsetX
- || s.shadowOffsetY != state.shadowOffsetY) {
- update = true;
+ break;
+ default:
+ break;
}
-
- if (update)
- state = s;
}
-}
-
-void QSGContext2DPrivate::scale(qreal x, qreal y)
-{
- state.matrix.scale(x, y);
- updateMatrix(state.matrix);
-}
-
-void QSGContext2DPrivate::rotate(qreal angle)
-{
- state.matrix.rotate(DEGREES(angle));
- updateMatrix(state.matrix);
-}
-
-
-void QSGContext2DPrivate::translate(qreal x, qreal y)
-{
- state.matrix.translate(x, y);
- updateMatrix(state.matrix);
-}
-void QSGContext2DPrivate::transform(
- qreal m11, qreal m12,
- qreal m21, qreal m22,
- qreal dx, qreal dy)
-{
- QMatrix matrix(m11, m12, m21, m22, dx, dy);
- state.matrix *= matrix;
- updateMatrix(state.matrix);
+ return args.This();
}
+/*!
+ \qmlclass QtQuick2::CanvasPixelArray
+ The CanvasPixelArray object provides ordered, indexed access to the color components of each pixel of the image data.
+ See http://www.w3.org/TR/2dcontext/#canvaspixelarray for more details.
+ */
-void QSGContext2DPrivate::setTransform(
- qreal m11, qreal m12,
- qreal m21, qreal m22,
- qreal dx, qreal dy)
+/*!
+ \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.
+ */
+v8::Handle<v8::Value> ctx2d_pixelArray_length(v8::Local<v8::String>, const v8::AccessorInfo &args)
{
- QMatrix matrix(m11, m12, m21, m22, dx, dy);
- state.matrix = matrix;
- updateMatrix(state.matrix);
-}
+ QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This());
+ if (!r || r->image.isNull()) return v8::Undefined();
-void QSGContext2DPrivate::clearRect(qreal x, qreal y,
- qreal w, qreal h)
-{
- commands.push_back(QSGContext2D::ClearRect);
- reals.push_back(x);
- reals.push_back(y);
- reals.push_back(w);
- reals.push_back(h);
+ return v8::Integer::New(r->image.width() * r->image.height() * 4);
}
-void QSGContext2DPrivate::fillRect(qreal x, qreal y,
- qreal w, qreal h)
+v8::Handle<v8::Value> ctx2d_pixelArray_indexed(uint32_t index, const v8::AccessorInfo& args)
{
- QRectF rect;
- if (tileRect.isValid()) {
- rect = tileRect.intersect(QRect(x, y, w, h));
- } else {
- rect = QRectF(x, y, w, h);
- }
+ QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(args.This());
- if (rect.isValid()) {
- commands.push_back(QSGContext2D::FillRect);
- reals.push_back(rect.x());
- reals.push_back(rect.y());
- reals.push_back(rect.width());
- reals.push_back(rect.height());
+ if (r && index && index < r->image.width() * r->image.height() * 4) {
+ const int w = r->image.width();
+ const int h = r->image.height();
+ const int row = (index / 4) / w;
+ const int 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();
}
-void QSGContext2DPrivate::strokeRect(qreal x, qreal y,
- qreal w, qreal h)
+v8::Handle<v8::Value> ctx2d_pixelArray_indexed_set(uint32_t index, v8::Local<v8::Value> value, const v8::AccessorInfo& info)
{
- QPainterPath p;
- p.addRect(x, y, w, h);
+ QV8Context2DPixelArrayResource *r = v8_resource_cast<QV8Context2DPixelArrayResource>(info.This());
- if (tileRect.isValid()) {
- QPainterPath tr;
- tr.addRect(tileRect);
- p = p.intersected(tr);
- }
+ const int v = value->Uint32Value();
+ if (r && index > 0 && index < r->image.width() * r->image.height() * 4 && v > 0 && v <= 255) {
+ const int w = r->image.width();
+ const int h = r->image.height();
+ const int row = (index / 4) / w;
+ const int col = (index / 4) % w;
- if (!p.isEmpty()) {
- commands.push_back(QSGContext2D::Stroke);
- pathes.push_back(p);
+ 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();
}
-
-void QSGContext2DPrivate::beginPath()
-{
- path = QPainterPath();
-}
-
-void QSGContext2DPrivate::closePath()
-{
- path.closeSubpath();
-}
-
-void QSGContext2DPrivate::moveTo( qreal x, qreal y)
-{
- path.moveTo(state.matrix.map(QPointF(x, y)));
-}
-
-void QSGContext2DPrivate::lineTo( qreal x, qreal y)
-{
- path.lineTo(state.matrix.map(QPointF(x, y)));
-}
-
-void QSGContext2DPrivate::quadraticCurveTo(qreal cpx, qreal cpy,
- qreal x, qreal y)
-{
- path.quadTo(state.matrix.map(QPointF(cpx, cpy)),
- state.matrix.map(QPointF(x, y)));
-}
-
-void QSGContext2DPrivate::bezierCurveTo(qreal cp1x, qreal cp1y,
- qreal cp2x, qreal cp2y,
- qreal x, qreal y)
-{
- path.cubicTo(state.matrix.map(QPointF(cp1x, cp1y)),
- state.matrix.map(QPointF(cp2x, cp2y)),
- state.matrix.map(QPointF(x, y)));
-}
-
-void QSGContext2DPrivate::arcTo(qreal x1, qreal y1,
- qreal x2, qreal y2,
- qreal radius)
-{
- QPointF st = state.matrix.map(QPoint(x1, y1));
- QPointF end = state.matrix.map(QPoint(x2, y2));
-
- path.arcTo(st.x(), st.y(),
- end.x()-st.x(), end.y()-st.y(),
- radius, 90);
-}
-
-void QSGContext2DPrivate::rect(qreal x, qreal y,
- qreal w, qreal h)
-{
- QPainterPath path;
- path.addRect(QRectF(x, y, w, h));
- path.addPath(state.matrix.map(path));
-}
-
-void QSGContext2DPrivate::arc(qreal xc,
- qreal yc,
- qreal radius,
- qreal sar,
- qreal ear,
- bool antiClockWise)
+/*!
+ \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)
{
- QPainterPath p;
-
- //### 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;
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
- double xs = xc - radius;
- double ys = yc - radius;
- double width = radius*2;
- double height = radius*2;
- if (!antiClockWise && (ea < sa)) {
- span += 360;
- } else if (antiClockWise && (sa < ea)) {
- span -= 360;
- }
+ QV8Engine *engine = V8ENGINE();
- //### 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 (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 (w > 0 && h > 0)
+ return qt_create_image_data(w, h, engine, QImage());
}
-
- p.moveTo(QPointF(xc + radius * qCos(sar),
- yc - radius * qSin(sar)));
-
- p.arcTo(xs, ys, width, height, sa, span);
-
- path.addPath(state.matrix.map(p));
+ return v8::Undefined();
}
-void QSGContext2DPrivate::fill()
+/*!
+ \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)
{
- QPainterPath tr;
- if (tileRect.isValid()) {
- tr.addRect(tileRect);
- tr = tr.intersected(path);
- } else {
- tr = path;
- }
-
- if (!tr.isEmpty()) {
- commands.push_back(QSGContext2D::Fill);
- pathes.push_back(tr);
- }
-}
+ QV8Context2DResource *r = v8_resource_cast<QV8Context2DResource>(args.This());
+ CHECK_CONTEXT(r)
-void QSGContext2DPrivate::stroke()
-{
- QPainterPath tr;
- if (tileRect.isValid()) {
- tr.addRect(tileRect);
- tr = tr.intersected(path);
- } else {
- tr = path;
- }
+ 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();
+ QImage image = r->context->canvas()->toImage(QRectF(x, y, w, h));
+ if (image.format() != QImage::Format_ARGB32)
+ image = image.convertToFormat(QImage::Format_ARGB32);
+ v8::Local<v8::Object> imageData = qt_create_image_data(w, h, engine, image);
- if (!tr.isEmpty()) {
- commands.push_back(QSGContext2D::Stroke);
- pathes.push_back(tr);
+ return imageData;
}
+ return v8::Null();
}
-void QSGContext2DPrivate::clip()
+/*!
+ \qmlmethod 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)
{
- //TODO:XXX
- state.clipPath = path;
- pathes.push_back(state.clipPath);
- commands.push_back(QSGContext2D::Clip);
-}
+ 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_ERROR("Context2D::putImageData, the image data type mismatch");
+ }
+ qreal dx = args[1]->NumberValue();
+ qreal dy = args[2]->NumberValue();
+ qreal w, h, dirtyX, dirtyY, dirtyWidth, dirtyHeight;
+
+ 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 (dirtyWidth < 0) {
+ dirtyX = dirtyX+dirtyWidth;
+ dirtyWidth = -dirtyWidth;
+ }
-void QSGContext2DPrivate::setGlobalAlpha( qreal alpha)
-{
- state.globalAlpha = alpha;
- commands.push_back(QSGContext2D::GlobalAlpha);
- reals.push_back(state.globalAlpha);
-}
+ if (dirtyHeight < 0) {
+ dirtyY = dirtyY+dirtyHeight;
+ dirtyHeight = -dirtyHeight;
+ }
-void QSGContext2DPrivate::setGlobalCompositeOperation( const QString &op)
-{
- state.globalCompositeOperation = compositeOperatorFromString(op);
- commands.push_back(QSGContext2D::GlobalCompositeOperation);
- ints.push_back(state.globalCompositeOperation);
-}
+ if (dirtyX < 0) {
+ dirtyWidth = dirtyWidth+dirtyX;
+ dirtyX = 0;
+ }
-void QSGContext2DPrivate::setStrokeStyle( const QVariant &style)
-{
- QSGCanvasGradient *gradient= qobject_cast<QSGCanvasGradient*>(style.value<QObject*>());
- QBrush b;
- if (gradient) {
- b = gradient->value();
- } else {
- b = colorFromString(style.toString());
- }
+ if (dirtyY < 0) {
+ dirtyHeight = dirtyHeight+dirtyY;
+ dirtyY = 0;
+ }
- if (state.strokeStyle != b) {
- state.strokeStyle = b;
- state.pen.setBrush(state.strokeStyle);
- commands.push_back(QSGContext2D::UpdatePen);
- pens.push_back(state.pen);
- }
-}
-void QSGContext2DPrivate::setStrokeColor(const QColor& color)
-{
- if (state.strokeStyle != color) {
- state.strokeStyle = color;
- commands.push_back(QSGContext2D::UpdatePen);
- QPen pen;
- pen.setBrush(state.strokeStyle);
- pens.push_back(pen);
- }
-}
+ if (dirtyX+dirtyWidth > w) {
+ dirtyWidth = w - dirtyX;
+ }
-void QSGContext2DPrivate::setFillColor(const QColor& color)
-{
- if (state.fillStyle != color) {
- state.fillStyle = color;
- commands.push_back(QSGContext2D::UpdateBrush);
- brushes.push_back(state.fillStyle);
- }
-}
+ if (dirtyY+dirtyHeight > h) {
+ dirtyHeight = h - dirtyY;
+ }
-void QSGContext2DPrivate::setFillStyle( const QVariant &style)
-{
- QSGCanvasGradient *gradient= qobject_cast<QSGCanvasGradient*>(style.value<QObject*>());
- QBrush b;
- if (gradient) {
- b = gradient->value();
- } else {
- b = colorFromString(style.toString());
- }
+ if (dirtyWidth <=0 || dirtyHeight <= 0)
+ return args.This();
+ } else {
+ dirtyX = 0;
+ dirtyY = 0;
+ dirtyWidth = w;
+ dirtyHeight = h;
+ }
- if (state.fillStyle != b) {
- state.fillStyle = b;
- commands.push_back(QSGContext2D::UpdateBrush);
- brushes.push_back(b);
+ 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.
+ */
-void QSGContext2DPrivate::setLineWidth( qreal w)
-{
- if (state.lineWidth != w) {
- state.pen.setWidthF(w);
- state.lineWidth = w;
- commands.push_back(QSGContext2D::UpdatePen);
- pens.push_back(state.pen);
- }
-}
-
-void QSGContext2DPrivate::setLineCap( const QString& cap)
+/*!
+ \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.
+ */
+static v8::Handle<v8::Value> ctx2d_gradient_addColorStop(const v8::Arguments &args)
{
- Qt::PenCapStyle style;
- if (cap == QLatin1String("round"))
- style = Qt::RoundCap;
- else if (cap == QLatin1String("square"))
- style = Qt::SquareCap;
- else //if (capString == "butt")
- style = Qt::FlatCap;
+ QV8Context2DStyleResource *style = v8_resource_cast<QV8Context2DStyleResource>(args.This());
+ if (!style)
+ V8THROW_ERROR("Not a CanvasGradient object");
+ QV8Engine *engine = V8ENGINE();
- if (state.lineCap != style) {
- state.pen.setCapStyle(style);
- state.lineCap = style;
- commands.push_back(QSGContext2D::UpdatePen);
- pens.push_back(state.pen);
- }
-}
+ if (args.Length() == 2) {
-void QSGContext2DPrivate::setLineJoin( const QString& join)
-{
- Qt::PenJoinStyle style;
- if (join == QLatin1String("round"))
- style = Qt::RoundJoin;
- else if (join == QLatin1String("bevel"))
- style = Qt::BevelJoin;
- else //if (joinString == "miter")
- style = Qt::MiterJoin;
- if (state.lineJoin != style) {
- state.lineJoin = style;
- state.pen.setJoinStyle(style);
- commands.push_back(QSGContext2D::UpdatePen);
- pens.push_back(state.pen);
- }
-}
+ 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 = qt_color_from_string(engine->toString(args[1]));
+ if (pos < 0.0 || pos > 1.0) {
+ //Throws an INDEX_SIZE_ERR exception
+ V8THROW_ERROR("CanvasGradient: parameter offset out of range");
+ }
-void QSGContext2DPrivate::setMiterLimit( qreal limit)
-{
- if (state.miterLimit != limit) {
- state.pen.setMiterLimit(limit);
- state.miterLimit = limit;
- commands.push_back(QSGContext2D::UpdatePen);
- pens.push_back(state.pen);
+ if (color.isValid()) {
+ gradient.setColorAt(pos, color);
+ } else {
+ //Throws a SYNTAX_ERR exception
+ V8THROW_ERROR("CanvasGradient: parameter color is not a valid color string");
+ }
+ style->brush = gradient;
}
-}
-void QSGContext2DPrivate::setShadowOffsetX( qreal x)
-{
- if (state.shadowOffsetX != x) {
- state.shadowOffsetX = x;
- commands.push_back(QSGContext2D::ShadowOffsetX);
- reals.push_back(x);
- }
+ return args.This();
}
-void QSGContext2DPrivate::setShadowOffsetY( qreal y)
-{
- if (state.shadowOffsetY != y) {
- state.shadowOffsetY = y;
- commands.push_back(QSGContext2D::ShadowOffsetY);
- reals.push_back(y);
- }
-}
-void QSGContext2DPrivate::setShadowBlur( qreal b)
-{
- if (state.shadowBlur != b) {
- state.shadowBlur = b;
- commands.push_back(QSGContext2D::ShadowBlur);
- reals.push_back(b);
- }
-}
-
-void QSGContext2DPrivate::setShadowColor( const QString& color)
+void QSGContext2D::beginPath()
{
- QColor c = colorFromString(color);
- if (state.shadowColor != c) {
- state.shadowColor = c;
- commands.push_back(QSGContext2D::ShadowColor);
- colors.push_back(c);
- }
+ m_path = QPainterPath();
+ m_path.setFillRule(state.fillRule);
}
-void QSGContext2DPrivate::setFont( const QString& fontString)
+void QSGContext2D::closePath()
{
- 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());
- } else
- font.setFamily(token);
- }
-
- if (state.font != font) {
- state.font = font;
- commands.push_back(QSGContext2D::Font);
- fonts.push_back(font);
- }
-}
+ if (m_path.isEmpty())
+ return;
-void QSGContext2DPrivate::setTextBaseline( const QString& baseline)
-{
- QSGContext2D::TextBaseLineType tbl;
- if (baseline==QLatin1String("alphabetic"))
- tbl = QSGContext2D::Alphabetic;
- else if (baseline == QLatin1String("hanging"))
- tbl = QSGContext2D::Hanging;
- else if (baseline == QLatin1String("top"))
- tbl = QSGContext2D::Top;
- else if (baseline == QLatin1String("bottom"))
- tbl = QSGContext2D::Bottom;
- else if (baseline == QLatin1String("middle"))
- tbl = QSGContext2D::Middle;
- else {
- tbl = QSGContext2D::Alphabetic;
- Q_Q(QSGContext2D);
- qmlInfo(q) << "QSGContext2D: invalid baseline:" << baseline;
- }
- if (state.textBaseline != tbl) {
- state.textBaseline = tbl;
- commands.push_back(QSGContext2D::TextBaseline);
- ints.push_back(tbl);
- }
+ QRectF boundRect = m_path.boundingRect();
+ if (boundRect.width() || boundRect.height())
+ m_path.closeSubpath();
}
-void QSGContext2DPrivate::setTextAlign(const QString& align)
+void QSGContext2D::moveTo( qreal x, qreal y)
{
- QSGContext2D::TextAlignType ta;
- if (align==QLatin1String("start"))
- ta = QSGContext2D::Start;
- else if (align == QLatin1String("end"))
- ta = QSGContext2D::End;
- else if (align == QLatin1String("left"))
- ta = QSGContext2D::Left;
- else if (align == QLatin1String("right"))
- ta = QSGContext2D::Right;
- else if (align == QLatin1String("center"))
- ta = QSGContext2D::Center;
- else {
- ta = QSGContext2D::Start;
- Q_Q(QSGContext2D);
- qmlInfo(q) << "QSGContext2D: invalid text align:" << align;
- }
- if (state.textAlign != ta) {
- state.textAlign = ta;
- commands.push_back(QSGContext2D::TextAlign);
- ints.push_back(ta);
- }
+ m_path.moveTo(state.matrix.map(QPointF(x, y)));
}
-void QSGContext2DPrivate::fillText(const QString& text, qreal x, qreal y)
+void QSGContext2D::lineTo( qreal x, qreal y)
{
- commands.push_back(QSGContext2D::FillText);
- strings.push_back(text);
- reals.push_back(x);
- reals.push_back(y);
- ints.push_back(state.textAlign);
- ints.push_back(state.textBaseline);
+ m_path.lineTo(state.matrix.map(QPointF(x, y)));
}
-
-void QSGContext2DPrivate::strokeText( const QString& text, qreal x, qreal y)
+void QSGContext2D::quadraticCurveTo(qreal cpx, qreal cpy,
+ qreal x, qreal y)
{
- commands.push_back(QSGContext2D::StrokeText);
- strings.push_back(text);
- reals.push_back(x);
- reals.push_back(y);
- ints.push_back(state.textAlign);
- ints.push_back(state.textBaseline);
+ m_path.quadTo(state.matrix.map(QPointF(cpx, cpy)),
+ state.matrix.map(QPointF(x, y)));
}
-void QSGContext2DPrivate::drawImage(const QString& url, qreal dx, qreal dy)
+void QSGContext2D::bezierCurveTo(qreal cp1x, qreal cp1y,
+ qreal cp2x, qreal cp2y,
+ qreal x, qreal y)
{
- commands.push_back(QSGContext2D::DrawImage1);
- strings.push_back(url);
- reals.push_back(dx);
- reals.push_back(dy);
+ m_path.cubicTo(state.matrix.map(QPointF(cp1x, cp1y)),
+ state.matrix.map(QPointF(cp2x, cp2y)),
+ state.matrix.map(QPointF(x, y)));
}
-void QSGContext2DPrivate::drawImage(const QString& url, qreal dx, qreal dy, qreal dw, qreal dh)
+void QSGContext2D::addArcTo(const QPointF& p1, const QPointF& p2, float radius)
{
- commands.push_back(QSGContext2D::DrawImage2);
- strings.push_back(url);
- reals.push_back(dx);
- reals.push_back(dy);
- reals.push_back(dw);
- reals.push_back(dh);
-}
+ QPointF p0(m_path.currentPosition());
-void QSGContext2DPrivate::drawImage(const QString& url, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh)
-{
- commands.push_back(QSGContext2D::DrawImage3);
- strings.push_back(url);
- reals.push_back(sx);
- reals.push_back(sy);
- reals.push_back(sw);
- reals.push_back(sh);
- reals.push_back(dx);
- reals.push_back(dy);
- reals.push_back(dw);
- reals.push_back(dh);
-}
+ 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());
-QList<int> QSGContext2DPrivate::getImageData(qreal sx, qreal sy, qreal sw, qreal sh)
-{
- Q_Q(QSGContext2D);
- waitingForPainting = true;
- commands.push_back(QSGContext2D::GetImageData);
- reals.push_back(sx);
- reals.push_back(sy);
- reals.push_back(sw);
- reals.push_back(sh);
- q->sync();
- return imageData;
-}
+ double cos_phi = (p1p0.x() * p1p2.x() + p1p0.y() * p1p2.y()) / (p1p0_length * p1p2_length);
-void QSGContext2DPrivate::putImageData(const QVariantList& imageData, qreal dx, qreal dy, qreal w, qreal h)
-{
- QImage image = cachedImage.copy(dx, dy, w, h);
- uchar* data = image.bits();
- int i = 0;
- while(i< imageData.size() && i < image.byteCount()) {
- //the stored order in QImage:BGRA
- //the stored order in Canvas:RGBA
- *(data+i) = imageData[i+2].toInt();//B
- *(data+i+1) = imageData[i+1].toInt();//G
- *(data+i+2) = imageData[i].toInt();//R
- *(data+i+3) = imageData[i+3].toInt();//A
- i+=4;
+ // 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;
}
- commands.push_back(QSGContext2D::PutImageData);
- images.push_back(image);
- reals.push_back(dx);
- reals.push_back(dy);
-}
-
-void QSGContext2D::save()
-{
- Q_D(QSGContext2D);
- d->save();
-}
-
-
-void QSGContext2D::restore()
-{
- Q_D(QSGContext2D);
- d->restore();
-}
-
-
-void QSGContext2D::scale(qreal x, qreal y)
-{
- Q_D(QSGContext2D);
- d->scale(x, y);
-}
-
-
-void QSGContext2D::rotate(qreal angle)
-{
- Q_D(QSGContext2D);
- d->rotate(angle);
-}
-
-
-void QSGContext2D::translate(qreal x, qreal y)
-{
- Q_D(QSGContext2D);
- d->translate(x, y);
-}
-
-void QSGContext2D::transform(qreal m11, qreal m12, qreal m21, qreal m22,
- qreal dx, qreal dy)
-{
- Q_D(QSGContext2D);
- d->transform(m11, m12, m21, m22, dx, dy);
-}
-
-void QSGContext2D::setTransform(qreal m11, qreal m12, qreal m21, qreal m22,
- qreal dx, qreal dy)
-{
- Q_D(QSGContext2D);
- d->setTransform(m11, m12, m21, m22, dx, dy);
-}
+ 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()));
-QString QSGContext2D::globalCompositeOperation() const
-{
- Q_D(const QSGContext2D);
- return compositeOperatorToString(d->state.globalCompositeOperation);
-}
+ 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;
-void QSGContext2D::setGlobalCompositeOperation(const QString &op)
-{
- Q_D(QSGContext2D);
- d->setGlobalCompositeOperation(op);
-}
+ // 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());
-QVariant QSGContext2D::strokeStyle() const
-{
- Q_D(const QSGContext2D);
- return d->state.strokeStyle;
-}
+ QPointF p((t_p1p0.x() + factor_ra * orth_p1p0.x()), (t_p1p0.y() + factor_ra * orth_p1p0.y()));
-void QSGContext2D::setStrokeStyle(const QVariant &style)
-{
- Q_D(QSGContext2D);
- d->setStrokeStyle(style);
-}
+ // 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;
-QVariant QSGContext2D::fillStyle() const
-{
- Q_D(const QSGContext2D);
- return d->state.fillStyle;
-}
+ // anticlockwise logic
+ bool anticlockwise = false;
-QColor QSGContext2D::strokeColor() const
-{
- Q_D(const QSGContext2D);
- return d->state.strokeStyle.color();
-}
+ 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;
-QColor QSGContext2D::fillColor() const
-{
- Q_D(const QSGContext2D);
- return d->state.fillStyle.color();
+ arc(p.x(), p.y(), radius, sa, ea, anticlockwise, false);
}
-void QSGContext2D::setFillStyle(const QVariant &style)
-{
- Q_D(QSGContext2D);
- d->setFillStyle(style);
-}
-void QSGContext2D::setStrokeColor(const QColor& color)
-{
- Q_D(QSGContext2D);
- d->setStrokeColor(color);
-}
-
-void QSGContext2D::setFillColor(const QColor& color)
-{
- Q_D(QSGContext2D);
- d->setFillColor(color);
-}
-
-qreal QSGContext2D::globalAlpha() const
-{
- Q_D(const QSGContext2D);
- return d->state.globalAlpha;
-}
-
-void QSGContext2D::setGlobalAlpha(qreal alpha)
-{
- Q_D(QSGContext2D);
- d->setGlobalAlpha(alpha);
-}
-
-QSGImage *QSGContext2D::createImage(const QString &url)
-{
- Q_D(QSGContext2D);
-//### cache image
- QSGImage* img = new QSGImage(d->canvas);
- img->setSource(QUrl(url));
- return img;
-}
-
-QSGCanvasGradient *QSGContext2D::createLinearGradient(qreal x0, qreal y0,
- qreal x1, qreal y1)
-{
- QLinearGradient g(x0, y0, x1, y1);
- return new QSGCanvasGradient(g);
-}
-
-
-QSGCanvasGradient *QSGContext2D::createRadialGradient(qreal x0, qreal y0,
- qreal r0, qreal x1,
- qreal y1, qreal r1)
-{
- QRadialGradient g(QPointF(x1, y1), r0+r1, QPointF(x0, y0));
- return new QSGCanvasGradient(g);
-}
-
-qreal QSGContext2D::lineWidth() const
-{
- Q_D(const QSGContext2D);
- return d->state.lineWidth;
-}
-
-void QSGContext2D::setLineWidth(qreal w)
-{
- Q_D(QSGContext2D);
- d->setLineWidth(w);
-}
-
-QString QSGContext2D::lineCap() const
-{
- Q_D(const QSGContext2D);
- switch(d->state.lineCap) {
- case Qt::RoundCap:
- return QLatin1String("round");
- case Qt::FlatCap:
- return QLatin1String("butt");
- case Qt::SquareCap:
- return QLatin1String("square");
- default:
- break;
- }
- return QLatin1String("");
-}
-
-void QSGContext2D::setLineCap(const QString &capString)
+void QSGContext2D::arcTo(qreal x1, qreal y1,
+ qreal x2, qreal y2,
+ qreal radius)
{
- Q_D(QSGContext2D);
- d->setLineCap(capString);
-}
+ QPointF st = state.matrix.map(QPointF(x1, y1));
+ QPointF end = state.matrix.map(QPointF(x2, y2));
-QString QSGContext2D::lineJoin() const
-{
- Q_D(const QSGContext2D);
- switch (d->state.lineJoin) {
- case Qt::RoundJoin:
- return QLatin1String("round");
- case Qt::BevelJoin:
- return QLatin1String("bevel");
- case Qt::MiterJoin:
- return QLatin1String("miter");
- default:
- break;
+ 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);
}
- return QLatin1String("");
}
-void QSGContext2D::setLineJoin(const QString &joinString)
-{
- Q_D(QSGContext2D);
- d->setLineJoin(joinString);
-}
-
-qreal QSGContext2D::miterLimit() const
-{
- Q_D(const QSGContext2D);
- return d->state.miterLimit;
-}
-
-void QSGContext2D::setMiterLimit(qreal m)
-{
- Q_D(QSGContext2D);
- d->setMiterLimit(m);
-}
-
-void QSGContext2D::setShadowOffsetX(qreal x)
-{
- Q_D(QSGContext2D);
- d->setShadowOffsetX(x);
-}
-
-void QSGContext2D::setShadowOffsetY(qreal y)
-{
- Q_D(QSGContext2D);
- d->setShadowOffsetY(y);
-}
-
-void QSGContext2D::setShadowBlur(qreal b)
-{
- Q_D(QSGContext2D);
- d->setShadowBlur(b);
-}
-
-void QSGContext2D::setShadowColor(const QString &str)
+void QSGContext2D::rect(qreal x, qreal y,
+ qreal w, qreal h)
{
- Q_D(QSGContext2D);
- d->setShadowColor(str);
+ m_path.addPolygon(state.matrix.map(QRectF(x, y, w, h)));
}
-QSGCanvasPath* QSGContext2D::path()
-{
- Q_D(const QSGContext2D);
- return new QSGCanvasPath(d->path, this);
-}
-void QSGContext2D::setPath(QSGCanvasPath* path)
+void QSGContext2D::roundedRect(qreal x, qreal y,
+ qreal w, qreal h,
+ qreal xr, qreal yr)
{
- Q_D(QSGContext2D);
- d->path = path->m_path;
+ QPainterPath path;
+ path.addRoundedRect(QRectF(x, y, w, h), xr, yr, Qt::AbsoluteSize);
+ m_path.addPath(state.matrix.map(path));
}
-QSGCanvasPath* QSGContext2D::createPath(const QString& pathString)
+void QSGContext2D::ellipse(qreal x, qreal y,
+ qreal w, qreal h)
{
QPainterPath path;
- if (QDeclarativeSvgParser::parsePathDataFast(pathString, path)) {
- return new QSGCanvasPath(path, this);
- }
- return 0;
+ path.addEllipse(x, y, w, h);
+ m_path.addPath(state.matrix.map(path));
}
-QString QSGContext2D::textBaseline() const
+void QSGContext2D::text(const QString& str, qreal x, qreal y)
{
- Q_D(const QSGContext2D);
- switch(d->state.textBaseline) {
- case QSGContext2D::Alphabetic:
- return QLatin1String("alphabetic");
- case QSGContext2D::Hanging:
- return QLatin1String("hanging");
- case QSGContext2D::Top:
- return QLatin1String("top");
- case QSGContext2D::Bottom:
- return QLatin1String("bottom");
- case QSGContext2D::Middle:
- return QLatin1String("middle");
- default:
- break;
- }
- return QLatin1String("alphabetic");
+ QPainterPath path;
+ path.addText(x, y, state.font, str);
+ m_path.addPath(state.matrix.map(path));
}
-void QSGContext2D::setTextBaseline(const QString &baseline)
+void QSGContext2D::arc(qreal xc,
+ qreal yc,
+ qreal radius,
+ qreal sar,
+ qreal ear,
+ bool antiClockWise,
+ bool transform)
{
- Q_D(QSGContext2D);
- d->setTextBaseline(baseline);
-}
-QString QSGContext2D::textAlign() const
-{
- Q_D(const QSGContext2D);
- switch(d->state.textAlign) {
- case QSGContext2D::Start:
- return QLatin1String("start");
- case QSGContext2D::End:
- return QLatin1String("end");
- case QSGContext2D::Left:
- return QLatin1String("left");
- case QSGContext2D::Right:
- return QLatin1String("right");
- case QSGContext2D::Center:
- return QLatin1String("center");
- default:
- break;
+ if (transform) {
+ QPointF point = state.matrix.map(QPointF(xc, yc));
+ xc = point.x();
+ yc = point.y();
}
- return QLatin1String("start");
-}
-
-void QSGContext2D::setTextAlign(const QString &align)
-{
- Q_D(QSGContext2D);
- d->setTextAlign(align);
-
- d->commands.push_back(QSGContext2D::TextAlign);
- d->ints.push_back(d->state.textAlign);
-}
-
-void QSGContext2D::setFont(const QString &fontString)
-{
- Q_D(QSGContext2D);
- d->setFont(fontString);
-}
-
-QString QSGContext2D::font() const
-{
- //### TODO
- Q_D(const QSGContext2D);
- return d->state.font.toString();
-}
-
-qreal QSGContext2D::shadowOffsetX() const
-{
- Q_D(const QSGContext2D);
- return d->state.shadowOffsetX;
-}
-
-qreal QSGContext2D::shadowOffsetY() const
-{
- Q_D(const QSGContext2D);
- return d->state.shadowOffsetY;
-}
+ //### 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
-qreal QSGContext2D::shadowBlur() const
-{
- Q_D(const QSGContext2D);
- return d->state.shadowBlur;
-}
+ float sa = DEGREES(sar);
+ float ea = DEGREES(ear);
+ double span = 0;
-QString QSGContext2D::shadowColor() const
-{
- Q_D(const QSGContext2D);
- return d->state.shadowColor.name();
-}
+ 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);
+ }
-void QSGContext2D::clearRect(qreal x, qreal y, qreal w, qreal h)
-{
- Q_D(QSGContext2D);
- d->clearRect(x, y, w, h);
-}
+ 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);
+ }
-void QSGContext2D::fillRect(qreal x, qreal y, qreal w, qreal h)
-{
- Q_D(QSGContext2D);
- d->fillRect(x, y, w, h);
+ m_path.arcTo(xs, ys, width, height, sa, span);
}
-int QSGContext2DPrivate::baseLineOffset(QSGContext2D::TextBaseLineType value, const QFontMetrics &metrics)
+int baseLineOffset(QSGContext2D::TextBaseLineType value, const QFontMetrics &metrics)
{
int offset = 0;
switch (value) {
- case QSGContext2D::Top:
+ case QSGContext2D::QSGContext2D::Top:
break;
- case QSGContext2D::Alphabetic:
- case QSGContext2D::Middle:
- case QSGContext2D::Hanging:
+ case QSGContext2D::QSGContext2D::Alphabetic:
+ case QSGContext2D::QSGContext2D::Middle:
+ case QSGContext2D::QSGContext2D::Hanging:
offset = metrics.ascent();
break;
- case QSGContext2D::Bottom:
+ case QSGContext2D::QSGContext2D::Bottom:
offset = metrics.height();
break;
}
return offset;
}
-int QSGContext2DPrivate::textAlignOffset(QSGContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &text)
+static int textAlignOffset(QSGContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &text)
{
int offset = 0;
if (value == QSGContext2D::Start)
- value = qApp->layoutDirection() == Qt::LeftToRight ? QSGContext2D::Left : QSGContext2D::Right;
+ value = QApplication::layoutDirection() == Qt::LeftToRight ? QSGContext2D::Left : QSGContext2D::Right;
else if (value == QSGContext2D::End)
- value = qApp->layoutDirection() == Qt::LeftToRight ? QSGContext2D::Right: QSGContext2D::Left;
+ value = QApplication::layoutDirection() == Qt::LeftToRight ? QSGContext2D::Right: QSGContext2D::Left;
switch (value) {
- case QSGContext2D::Center:
+ case QSGContext2D::QSGContext2D::Center:
offset = metrics.width(text)/2;
break;
- case QSGContext2D::Right:
+ case QSGContext2D::QSGContext2D::Right:
offset = metrics.width(text);
- case QSGContext2D::Left:
+ case QSGContext2D::QSGContext2D::Left:
default:
break;
}
return offset;
}
-void QSGContext2D::fillText(const QString &text, qreal x, qreal y)
-{
- Q_D(QSGContext2D);
- d->fillText(text, x, y);
-}
-
-void QSGContext2D::strokeText(const QString &text, qreal x, qreal y)
-{
- Q_D(QSGContext2D);
- d->strokeText(text, x, y);
-}
-void QSGContext2D::strokeRect(qreal x, qreal y, qreal w, qreal h)
+QImage QSGContext2D::createImage(const QUrl& url)
{
- Q_D(QSGContext2D);
- d->strokeRect(x, y, w, h);
+ return m_canvas->loadedImage(url);
}
-void QSGContext2D::beginPath()
+QPainterPath QSGContext2D::createTextGlyphs(qreal x, qreal y, const QString& text)
{
- Q_D(QSGContext2D);
- d->beginPath();
-}
+ const QFontMetrics metrics(state.font);
+ int yoffset = baseLineOffset(static_cast<QSGContext2D::TextBaseLineType>(state.textBaseline), metrics);
+ int xoffset = textAlignOffset(static_cast<QSGContext2D::TextAlignType>(state.textAlign), metrics, text);
+ QPainterPath textPath;
-void QSGContext2D::closePath()
-{
- Q_D(QSGContext2D);
- d->closePath();
-}
-
-
-void QSGContext2D::moveTo(qreal x, qreal y)
-{
- Q_D(QSGContext2D);
- d->moveTo(x, y);
-}
-
-
-void QSGContext2D::lineTo(qreal x, qreal y)
-{
- Q_D(QSGContext2D);
- d->lineTo(x, y);
-}
-
-
-void QSGContext2D::quadraticCurveTo(qreal cpx, qreal cpy, qreal x, qreal y)
-{
- Q_D(QSGContext2D);
- d->quadraticCurveTo(cpx, cpy, x, y);
-}
-
-
-void QSGContext2D::bezierCurveTo(qreal cp1x, qreal cp1y,
- qreal cp2x, qreal cp2y, qreal x, qreal y)
-{
- Q_D(QSGContext2D);
- d->bezierCurveTo(cp1x, cp1y, cp2x, cp2y, x, y);
-}
-
-
-void QSGContext2D::arcTo(qreal x1, qreal y1, qreal x2, qreal y2, qreal radius)
-{
- Q_D(QSGContext2D);
- d->arcTo(x1, y1, x2, y2, radius);
-}
-
-
-void QSGContext2D::rect(qreal x, qreal y, qreal w, qreal h)
-{
- Q_D(QSGContext2D);
- d->rect(x, y, w, h);
-}
-
-void QSGContext2D::arc(qreal xc, qreal yc, qreal radius,
- qreal sar, qreal ear,
- bool anticlockwise)
-{
- Q_D(QSGContext2D);
- d->arc(xc, yc, radius, sar, ear, anticlockwise);
-}
-
-
-void QSGContext2D::fill()
-{
- Q_D(QSGContext2D);
- d->fill();
-}
-
-
-void QSGContext2D::stroke()
-{
- Q_D(QSGContext2D);
- d->stroke();
-}
-
-
-void QSGContext2D::clip()
-{
- Q_D(QSGContext2D);
- d->clip();
+ textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), state.font, text);
+ textPath = state.matrix.map(textPath);
+ return textPath;
}
bool QSGContext2D::isPointInPath(qreal x, qreal y) const
{
- Q_D(const QSGContext2D);
- return d->path.contains(QPointF(x, y));
-}
-
-void QSGContext2D::setPathString(const QString& path)
-{
- Q_D(QSGContext2D);
- d->path = QPainterPath();
- QDeclarativeSvgParser::parsePathDataFast(path, d->path);
-}
-
-QList<int> QSGContext2D::getImageData(qreal sx, qreal sy, qreal sw, qreal sh)
-{
- Q_D(QSGContext2D);
- return d->getImageData(sx, sy, sw, sh);
-}
-
-void QSGContext2D::putImageData(const QVariant& imageData, qreal x, qreal y, qreal w, qreal h)
-{
- Q_D(QSGContext2D);
- return d->putImageData(imageData.toList(), x, y, w, h);
-}
-
-QSGContext2D::QSGContext2D(QObject *parent)
- : QObject(*(new QSGContext2DPrivate()), parent)
-{
- Q_D(QSGContext2D);
- d->canvas = qobject_cast<QSGCanvasItem*>(parent);
+ return m_path.contains(QPointF(x, y));
}
-QSGContext2D::QSGContext2D(QSGContext2D *orig, QSGContext2DWorkerAgent* agentData)
- : QObject(*(new QSGContext2DPrivate()), 0)
+QSGContext2D::QSGContext2D(QSGCanvasItem* item)
+ : m_canvas(item)
+ , m_buffer(new QSGContext2DCommandBuffer)
+ , m_v8engine(0)
{
- Q_D(QSGContext2D);
- d->agent = 0;
- d->agentData = agentData;
- if (d->agentData) {
- d->agentData->orig = orig;
- }
- d->canvas = qobject_cast<QSGCanvasItem*>(orig);
+ reset();
}
-QSGCanvasItem* QSGContext2D::canvas() const
-{
- Q_D(const QSGContext2D);
- return d->canvas;
-}
QSGContext2D::~QSGContext2D()
{
- Q_D(QSGContext2D);
- if (d->agent) {
- d->agentData->syncDone.wakeAll();
- d->agent->release();
- }
-}
-
-bool QSGContext2D::isDirty() const
-{
- Q_D(const QSGContext2D);
- return !d->commands.isEmpty();
}
v8::Handle<v8::Object> QSGContext2D::v8value() const
{
- Q_D(const QSGContext2D);
- return d->v8value;
+ return m_v8value;
}
-class QSGContext2DEngineData : public QV8Engine::Deletable
-{
-public:
- QSGContext2DEngineData(QV8Engine *engine);
- ~QSGContext2DEngineData();
-
- v8::Persistent<v8::Function> constructor;
-};
-
QSGContext2DEngineData::QSGContext2DEngineData(QV8Engine *engine)
{
v8::HandleScope handle_scope;
@@ -2653,23 +2872,22 @@ QSGContext2DEngineData::QSGContext2DEngineData(QV8Engine *engine)
v8::Local<v8::FunctionTemplate> ft = v8::FunctionTemplate::New();
ft->InstanceTemplate()->SetHasExternalResource(true);
- ft->PrototypeTemplate()->Set(v8::String::New("sync"), V8FUNCTION(ctx2d_sync, engine));
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->InstanceTemplate()->SetAccessor(v8::String::New("valid"), ctx2d_valid, 0, v8::External::Wrap(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->InstanceTemplate()->SetAccessor(v8::String::New("fillColor"), ctx2d_fillColor, ctx2d_fillColor_set, v8::External::Wrap(engine));
- ft->InstanceTemplate()->SetAccessor(v8::String::New("strokeColor"), ctx2d_strokeColor, ctx2d_strokeColor_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("createPattern"), V8FUNCTION(ctx2d_createPattern, engine));
@@ -2681,6 +2899,7 @@ QSGContext2DEngineData::QSGContext2DEngineData(QV8Engine *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));
@@ -2695,761 +2914,169 @@ QSGContext2DEngineData::QSGContext2DEngineData(QV8Engine *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("setPathString"), V8FUNCTION(ctx2d_setPathString, 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("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));
- constructor = qPersistentNew(ft->GetFunction());
-}
-
-QSGContext2DEngineData::~QSGContext2DEngineData()
-{
- qPersistentDispose(constructor);
-}
-
-V8_DEFINE_EXTENSION(QSGContext2DEngineData, engineData);
-
-QV8Engine* QSGContext2D::v8Engine() const
-{
- Q_D(const QSGContext2D);
- return d->v8engine;
-}
-
-void QSGContext2D::setV8Engine(QV8Engine *engine)
-{
- v8::HandleScope handle_scope;
- v8::Context::Scope scope(engine->context());
-
- Q_D(QSGContext2D);
- if (d->v8engine != engine) {
- d->v8engine = engine;
+ constructorContext = qPersistentNew(ft->GetFunction());
- qPersistentDispose(d->v8value);
+ 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());
- if (d->v8engine == 0)
- return;
+ v8::Local<v8::FunctionTemplate> ftPattern = v8::FunctionTemplate::New();
+ ftPattern->InstanceTemplate()->SetHasExternalResource(true);
+ constructorPattern = qPersistentNew(ftPattern->GetFunction());
- QSGContext2DEngineData *ed = engineData(engine);
- d->v8value = qPersistentNew(ed->constructor->NewInstance());
- QV8Context2DResource *r = new QV8Context2DResource(engine);
- r->context = this;
- d->v8value->SetExternalResource(r);
- }
-}
+ 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());
-bool QSGContext2D::valid() const
-{
- Q_D(const QSGContext2D);
- return d->valid;
+ 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());
}
-void QSGContext2D::setValid(bool valid)
-{
- Q_D(QSGContext2D);
- d->valid = valid;
-}
-void QSGContext2D::setTileRect(const QRectF& rect)
-{
- Q_D(QSGContext2D);
- if (d->tileRect != rect)
- d->tileRect = rect;
-}
-void QSGContext2D::addref()
+QSGContext2DEngineData::~QSGContext2DEngineData()
{
- Q_D(QSGContext2D);
- Q_ASSERT(d->agentData);
- d->agentData->ref.ref();
+ qPersistentDispose(constructorContext);
+ qPersistentDispose(constructorGradient);
+ qPersistentDispose(constructorPattern);
+ qPersistentDispose(constructorImageData);
+ qPersistentDispose(constructorPixelArray);
}
-void QSGContext2D::release()
+void QSGContext2D::popState()
{
- Q_D(QSGContext2D);
- Q_ASSERT(d->agentData);
- if (!d->agentData->ref.deref()) {
- deleteLater();
- }
-}
+ if (m_stateStack.isEmpty())
+ return;
-void QSGContext2D::processCommands(const QJSValue& commands)
-{
-#ifdef QSGCANVASITEM_DEBUG
- QElapsedTimer t;
- t.start();
-#endif
- int ii = 0;
- if (commands.isArray()) {
- QJSValue cmd = commands.property(ii);
- while(cmd.isValid()) {
- processCommand(cmd);
- ii++;
- cmd = commands.property(ii);
- }
- }
+ QSGContext2D::State newState = m_stateStack.pop();
-#ifdef QSGCANVASITEM_DEBUG
- qDebug() << "processed" << ii << "commands in " << t.nsecsElapsed() << "nsecs";
-#endif
- sync();
-}
+ if (state.matrix != newState.matrix)
+ buffer()->updateMatrix(newState.matrix);
-void QSGContext2D::sync()
-{
- Q_D(QSGContext2D);
+ if (newState.globalAlpha != state.globalAlpha)
+ buffer()->setGlobalAlpha(newState.globalAlpha);
-#ifdef QSGCANVASITEM_DEBUG
- QElapsedTimer t;
- t.start();
-#endif
- if (d->agentData) {
- if (d->agentData->ref == 1) return;
+ if (newState.globalCompositeOperation != state.globalCompositeOperation)
+ buffer()->setGlobalCompositeOperation(newState.globalCompositeOperation);
- Sync *s = new Sync;
- s->data = d->agentData;
+ if (newState.fillStyle != state.fillStyle)
+ buffer()->setFillStyle(newState.fillStyle);
- d->agentData->mutex.lock();
- QCoreApplication::postEvent(this, s);
- d->agentData->syncDone.wait(&d->agentData->mutex);
- d->agentData->mutex.unlock();
- } else {
- //qmlInfo(this) << "Context2D sync() can only be called from a WorkerScript;";
- emit changed();
- }
-
-#ifdef QSGCANVASITEM_DEBUG
- qDebug() << "syncing time:" << t.nsecsElapsed();
-#endif
-}
-
-
-bool QSGContext2D::event(QEvent *e)
-{
- Q_D(QSGContext2D);
- if (e->type() == QEvent::User && d->agentData) {
- QMutexLocker locker(&d->agentData->mutex);
- Sync *s = static_cast<Sync *>(e);
-
- QSGContext2DPrivate* origin_d = static_cast<QSGContext2DPrivate*>(s->data->orig->d_func());
-
- //quick copy
- memcpy_vector<PaintCommand>(&origin_d->commands, d->commands);
- memcpy_vector<int>(&origin_d->ints, d->ints);
- memcpy_vector<qreal>(&origin_d->reals, d->reals);
- memcpy_vector<QColor>(&origin_d->colors, d->colors);
- memcpy_vector<QMatrix>(&origin_d->matrixes, d->matrixes);
- memcpy_vector<QSize>(&origin_d->sizes, d->sizes);
-
- //slow copy
- copy_vector<QString>(&origin_d->strings, d->strings);
- copy_vector<QVariant>(&origin_d->variants, d->variants);
- copy_vector<QPen>(&origin_d->pens, d->pens);
- copy_vector<QBrush>(&origin_d->brushes, d->brushes);
- copy_vector<QPainterPath>(&origin_d->pathes, d->pathes);
- copy_vector<QFont>(&origin_d->fonts, d->fonts);
- copy_vector<QImage>(&origin_d->images, d->images);
- origin_d->state = d->state;
- d->clearCommands();
-
- if (d->waitingForPainting) {
- d->imageData.clear();
- origin_d->imageData.clear();
- emit s->data->orig->changed();
- while(origin_d->imageData.isEmpty()) {
- QCoreApplication::processEvents();
- }
- d->imageData = origin_d->imageData;
- d->waitingForPainting = false;
- qDebug() << "imageData size:" << d->imageData.size();
- } else {
- emit s->data->orig->changed();
- }
+ if (newState.strokeStyle != state.strokeStyle)
+ buffer()->setStrokeStyle(newState.strokeStyle);
- d->agentData->syncDone.wakeAll();
- return true;
- }
- return QObject::event(e);
-}
+ if (newState.lineWidth != state.lineWidth)
+ buffer()->setLineWidth(newState.lineWidth);
-void QSGContext2D::processCommand(const QJSValue& cmd)
-{
- int action = cmd.property(0).toInt32();
- switch (action) {
- case QSGContext2D::Save:
- save();
- break;
- case QSGContext2D::Restore:
- restore();
- break;
- case QSGContext2D::Scale:
- scale(cmd.property(1).toNumber(), cmd.property(2).toNumber());
- break;
- case QSGContext2D::Rotate:
- rotate(cmd.property(1).toNumber());
- break;
- case QSGContext2D::Translate:
- translate(cmd.property(1).toNumber(), cmd.property(2).toNumber());
- break;
- case QSGContext2D::Transform:
- transform(cmd.property(1).toNumber(),
- cmd.property(2).toNumber(),
- cmd.property(3).toNumber(),
- cmd.property(4).toNumber(),
- cmd.property(5).toNumber(),
- cmd.property(6).toNumber());
- break;
- case QSGContext2D::SetTransform:
- setTransform(cmd.property(1).toNumber(),
- cmd.property(2).toNumber(),
- cmd.property(3).toNumber(),
- cmd.property(4).toNumber(),
- cmd.property(5).toNumber(),
- cmd.property(6).toNumber());
- break;
- case QSGContext2D::ClearRect:
- clearRect(cmd.property(1).toNumber(),
- cmd.property(2).toNumber(),
- cmd.property(3).toNumber(),
- cmd.property(4).toNumber());
- break;
- case QSGContext2D::FillRect:
- fillRect(cmd.property(1).toNumber(),
- cmd.property(2).toNumber(),
- cmd.property(3).toNumber(),
- cmd.property(4).toNumber());
- break;
- case QSGContext2D::StrokeRect:
- strokeRect(cmd.property(1).toNumber(),
- cmd.property(2).toNumber(),
- cmd.property(3).toNumber(),
- cmd.property(4).toNumber());
- break;
- case QSGContext2D::BeginPath:
- beginPath();
- break;
- case QSGContext2D::ClosePath:
- closePath();
- break;
- case QSGContext2D::MoveTo:
- moveTo(cmd.property(1).toNumber(),
- cmd.property(2).toNumber());
- break;
- case QSGContext2D::LineTo:
- lineTo(cmd.property(1).toNumber(),
- cmd.property(2).toNumber());
- break;
- case QSGContext2D::QuadraticCurveTo:
- quadraticCurveTo(cmd.property(1).toNumber(),
- cmd.property(2).toNumber(),
- cmd.property(3).toNumber(),
- cmd.property(4).toNumber());
- break;
- case QSGContext2D::BezierCurveTo:
- bezierCurveTo(cmd.property(1).toNumber(),
- cmd.property(2).toNumber(),
- cmd.property(3).toNumber(),
- cmd.property(4).toNumber(),
- cmd.property(5).toNumber(),
- cmd.property(6).toNumber());
- break;
- case QSGContext2D::ArcTo:
- arcTo(cmd.property(1).toNumber(),
- cmd.property(2).toNumber(),
- cmd.property(3).toNumber(),
- cmd.property(4).toNumber(),
- cmd.property(5).toNumber());
- break;
- case QSGContext2D::Rect:
- rect(cmd.property(1).toNumber(),
- cmd.property(2).toNumber(),
- cmd.property(3).toNumber(),
- cmd.property(4).toNumber());
- break;
- case QSGContext2D::Arc:
- arc(cmd.property(1).toNumber(),
- cmd.property(2).toNumber(),
- cmd.property(3).toNumber(),
- cmd.property(4).toNumber(),
- cmd.property(5).toNumber(),
- cmd.property(6).toBool());
- break;
- case QSGContext2D::Fill:
- fill();
- break;
- case QSGContext2D::Stroke:
- stroke();
- break;
- case QSGContext2D::Clip:
- clip();
- break;
- case QSGContext2D::GlobalAlpha:
- setGlobalAlpha(cmd.property(1).toNumber());
- break;
- case QSGContext2D::GlobalCompositeOperation:
- setGlobalCompositeOperation(cmd.property(1).toString());
- break;
- case QSGContext2D::StrokeStyle:
- setStrokeStyle(cmd.property(1).toVariant());
- break;
- case QSGContext2D::FillStyle:
- setFillStyle(cmd.property(1).toVariant());
- break;
- case QSGContext2D::FillColor:
- setFillColor(cmd.property(1).toVariant().value<QColor>());
- break;
- case QSGContext2D::StrokeColor:
- setStrokeColor(cmd.property(1).toVariant().value<QColor>());
- break;
- case QSGContext2D::LineWidth:
- setLineWidth(cmd.property(1).toNumber());
- break;
- case QSGContext2D::LineCap:
- setLineCap(cmd.property(1).toString());
- break;
- case QSGContext2D::LineJoin:
- setLineJoin(cmd.property(1).toString());
- break;
- case QSGContext2D::MiterLimit:
- setMiterLimit(cmd.property(1).toNumber());
- break;
- case QSGContext2D::ShadowOffsetX:
- setShadowOffsetX(cmd.property(1).toNumber());
- break;
- case QSGContext2D::ShadowOffsetY:
- setShadowOffsetY(cmd.property(1).toNumber());
- break;
- case QSGContext2D::ShadowBlur:
- setShadowBlur(cmd.property(1).toNumber());
- break;
- case QSGContext2D::ShadowColor:
- setShadowColor(cmd.property(1).toString());
- break;
- case QSGContext2D::Font:
- setFont(cmd.property(1).toString());
- break;
- case QSGContext2D::TextBaseline:
- setTextBaseline(cmd.property(1).toString());
- break;
- case QSGContext2D::TextAlign:
- setTextAlign(cmd.property(1).toString());
- break;
- case QSGContext2D::FillText:
- fillText(cmd.property(1).toString(), cmd.property(2).toNumber(), cmd.property(3).toNumber());
- break;
- case QSGContext2D::StrokeText:
- strokeText(cmd.property(1).toString(), cmd.property(2).toNumber(), cmd.property(3).toNumber());
- break;
- case QSGContext2D::DrawImage1:
- {
- drawImage(cmd.property(1).toString(),
- cmd.property(2).toNumber(),
- cmd.property(3).toNumber());
- break;
- }
- case QSGContext2D::DrawImage2:
- drawImage(cmd.property(1).toString(),
- cmd.property(2).toNumber(),
- cmd.property(3).toNumber(),
- cmd.property(4).toNumber(),
- cmd.property(5).toNumber());
- break;
- case QSGContext2D::DrawImage3:
- drawImage(cmd.property(1).toString(),
- cmd.property(2).toNumber(),
- cmd.property(3).toNumber(),
- cmd.property(4).toNumber(),
- cmd.property(5).toNumber(),
- cmd.property(6).toNumber(),
- cmd.property(7).toNumber(),
- cmd.property(8).toNumber(),
- cmd.property(9).toNumber());
- break;
- case QSGContext2D::PutImageData:
- putImageData(cmd.property(1).toVariant(),
- cmd.property(2).toNumber(),
- cmd.property(3).toNumber(),
- cmd.property(4).toNumber(),
- cmd.property(5).toNumber());
- break;
- default:
- break;
- }
-}
+ if (newState.lineCap != state.lineCap)
+ buffer()->setLineCap(newState.lineCap);
-void QSGContext2D::paint(QPainter* p)
-{
- Q_D(QSGContext2D);
+ if (newState.lineJoin != state.lineJoin)
+ buffer()->setLineJoin(newState.lineJoin);
- QMatrix originMatrix = p->matrix();
- if (!d->commands.isEmpty()) {
- int matrix_idx, real_idx, int_idx, variant_idx, string_idx,color_idx,cmd_idx,
- pen_idx, brush_idx, font_idx, path_idx, image_idx, size_idx;
+ if (newState.miterLimit != state.miterLimit)
+ buffer()->setMiterLimit(newState.miterLimit);
- matrix_idx = real_idx = int_idx = variant_idx = string_idx =color_idx = cmd_idx
- = pen_idx = brush_idx = font_idx = path_idx = image_idx = size_idx = 0;
+ if (newState.clipPath != state.clipPath)
+ buffer()->clip(newState.clipPath);
- foreach(PaintCommand cmd, d->commands) {
- switch (cmd) {
- case UpdateMatrix:
- {
- d->state.matrix = d->matrixes[matrix_idx++];
- p->setMatrix(d->state.matrix * originMatrix);
- break;
- }
- case ClearRect:
- {
- qreal x = d->reals[real_idx++];
- qreal y = d->reals[real_idx++];
- qreal w = d->reals[real_idx++];
- qreal h = d->reals[real_idx++];
- p->eraseRect(QRectF(x, y, w, h));
- break;
- }
- case FillRect:
- {
- qreal x = d->reals[real_idx++];
- qreal y = d->reals[real_idx++];
- qreal w = d->reals[real_idx++];
- qreal h = d->reals[real_idx++];
- if (d->hasShadow())
- d->fillRectShadow(p, QRectF(x, y, w, h));
- else
- p->fillRect(QRectF(x, y, w, h), p->brush());
- break;
- }
- case ShadowColor:
- {
- QColor c = d->colors[color_idx++];
- d->state.shadowColor = c;
- break;
- }
- case ShadowBlur:
- {
- qreal blur = d->reals[real_idx++];
- d->state.shadowBlur = blur;
- break;
- }
- case ShadowOffsetX:
- {
- qreal x = d->reals[real_idx++];
- d->state.shadowOffsetX = x;
- break;
- }
- case ShadowOffsetY:
- {
- qreal y = d->reals[real_idx++];
- d->state.shadowOffsetY = y;
- break;
- }
- case Fill:
- {
- QPainterPath path = d->pathes[path_idx++];
- //qDebug() << "fill path:" << path.elementCount();
- if (d->hasShadow())
- d->fillShadowPath(p,path);
- else
- p->fillPath(path, p->brush());
- break;
- }
- case Stroke:
- {
- //p->setMatrix(d->state.matrix);
- //QPainterPath path = d->state.matrix.inverted().map(d->pathes[path_idx++]);
- //qDebug() << "stroke path:" << path.elementCount();
- QPainterPath path = d->pathes[path_idx++];
- if (d->hasShadow())
- d->strokeShadowPath(p,path);
- else
- p->strokePath(path, p->pen());
- break;
- }
- case Clip:
- {
- QPainterPath clipPath = d->pathes[path_idx++];
- p->setClipPath(clipPath);
- p->setClipping(true);
- break;
- }
- case UpdateBrush:
- {
- p->setBrush(d->brushes[brush_idx++]);
- break;
- }
- case UpdatePen:
- {
- p->setPen(d->pens[pen_idx++]);
- break;
- }
- case GlobalAlpha:
- {
- p->setOpacity(d->reals[real_idx++]);
- break;
- }
- case GlobalCompositeOperation:
- {
- p->setCompositionMode(static_cast<QPainter::CompositionMode>(d->ints[int_idx++]));
- break;
- }
- case Font:
- {
- p->setFont(d->fonts[font_idx++]);
- break;
- }
- case StrokeText:
- {
- QString text = d->strings[string_idx++];
- qreal x = d->reals[real_idx++];
- qreal y = d->reals[real_idx++];
- int align = d->ints[int_idx++];
- int baseline = d->ints[int_idx++];
-
- QPen oldPen = p->pen();
- p->setPen(QPen(p->brush(),0));
- //p->setMatrix(state.matrix, false); // always set?
-
- QPainterPath textPath;
- QFont oldFont = p->font();
- QFont font = p->font();
- font.setStyleStrategy(QFont::ForceOutline);
- p->setFont(font);
- const QFontMetrics &metrics = p->fontMetrics();
- int yoffset = d->baseLineOffset(static_cast<QSGContext2D::TextBaseLineType>(baseline), metrics);
- int xoffset = d->textAlignOffset(static_cast<QSGContext2D::TextAlignType>(align), metrics, text);
- textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), font, text);
- if (d->hasShadow())
- d->strokeShadowPath(p,textPath);
-
- p->strokePath(textPath, QPen(p->brush(), p->pen().widthF()));
-
- //reset old font
- p->setFont(oldFont);
- p->setPen(oldPen);
- break;
- }
- case FillText:
- {
- QString text = d->strings[string_idx++];
- qreal x = d->reals[real_idx++];
- qreal y = d->reals[real_idx++];
- int align = d->ints[int_idx++];
- int baseline = d->ints[int_idx++];
-
- QFont oldFont = p->font();
- QPen oldPen = p->pen();
- p->setPen(QPen(p->brush(), p->pen().widthF()));
- //p->setMatrix(state.matrix, false);
- //QFont font = p->font();
- QFont font = d->state.font;
- font.setBold(true);
-
- p->setFont(font);
- int yoffset = d->baseLineOffset(static_cast<QSGContext2D::TextBaseLineType>(baseline), p->fontMetrics());
- int xoffset = d->textAlignOffset(static_cast<QSGContext2D::TextAlignType>(align), p->fontMetrics(), text);
- QTextOption opt; // Adjust baseLine etc
- if (d->hasShadow()) {
- const QFontMetrics &metrics = p->fontMetrics();
- QPainterPath textPath;
- textPath.addText(x - xoffset, y - yoffset+metrics.ascent(), font, text);
- d->fillShadowPath(p,textPath);
- }
- //p->drawText(QRectF(x - xoffset, y - yoffset, QWIDGETSIZE_MAX, p->fontMetrics().height()), text, opt);
- p->setFont(oldFont);
- p->setPen(oldPen);
- break;
- }
- case DrawImage1:
- {
- QUrl url(d->strings[string_idx++]);
- qreal x = d->reals[real_idx++];
- qreal y = d->reals[real_idx++];
- QDeclarativePixmap px(qmlEngine(d->canvas), url);
- qDebug() << "draw image:" << url << px.pixmap().size();
- if (px.isReady()) {
- QPixmap pixmap = px.pixmap();
- if (d->hasShadow()) {
- QImage shadow = d->makeShadowImage(pixmap);
- qreal dx = x + (d->state.shadowOffsetX < 0? d->state.shadowOffsetX:0);
- qreal dy = y + (d->state.shadowOffsetY < 0? d->state.shadowOffsetY:0);
- p->drawImage(QPointF(dx, dy), shadow);
- }
- p->drawPixmap(QPointF(x, y), pixmap);
- }
- break;
- }
- case DrawImage2:
- {
- qreal dx = d->reals[real_idx++];
- qreal dy = d->reals[real_idx++];
- qreal dw = d->reals[real_idx++];
- qreal dh = d->reals[real_idx++];
- QUrl url(d->strings[string_idx++]);
- QDeclarativePixmap px(qmlEngine(d->canvas), url);
- if (px.isReady()) {
- QPixmap pixmap = px.pixmap().scaled(dw, dh);
- if (d->hasShadow()) {
- QImage shadow = d->makeShadowImage(pixmap);
- qreal shadow_dx = dx + (d->state.shadowOffsetX < 0? d->state.shadowOffsetX:0);
- qreal shadow_dy = dy + (d->state.shadowOffsetY < 0? d->state.shadowOffsetY:0);
- p->drawImage(QPointF(shadow_dx, shadow_dy), shadow);
- }
- p->drawPixmap(QPointF(dx, dy), pixmap);
- }
- break;
- }
- case DrawImage3:
- {
- qreal sx = d->reals[real_idx++];
- qreal sy = d->reals[real_idx++];
- qreal sw = d->reals[real_idx++];
- qreal sh = d->reals[real_idx++];
- qreal dx = d->reals[real_idx++];
- qreal dy = d->reals[real_idx++];
- qreal dw = d->reals[real_idx++];
- qreal dh = d->reals[real_idx++];
- QUrl url(d->strings[string_idx++]);
- QDeclarativePixmap px(qmlEngine(d->canvas), url);
- if (px.isReady()) {
- QPixmap pixmap = px.pixmap().copy(sx, sy, sw, sh).scaled(dw, dh);
- if (d->hasShadow()) {
- QImage shadow = d->makeShadowImage(pixmap);
- qreal shadow_dx = dx + (d->state.shadowOffsetX < 0? d->state.shadowOffsetX:0);
- qreal shadow_dy = dy + (d->state.shadowOffsetY < 0? d->state.shadowOffsetY:0);
- p->drawImage(QPointF(shadow_dx, shadow_dy), shadow);
- }
- p->drawPixmap(QPointF(dx, dy), pixmap);
- }
- break;
- }
- case GetImageData:
- {
- qreal sx = d->reals[real_idx++];
- qreal sy = d->reals[real_idx++];
- qreal sw = d->reals[real_idx++];
- qreal sh = d->reals[real_idx++];
- QImage img = toImage().copy(sx, sy, sw, sh);
- const uchar* data = img.constBits();
- int i = 0;
-
- while(i< img.byteCount()) {
- //the stored order in QImage:BGRA
- d->imageData << *(data+i+2);//R
- d->imageData << *(data+i+1);//G
- d->imageData << *(data+i);//B
- d->imageData << *(data+i+3);//A
- i+=4;
- }
- break;
- }
- case PutImageData:
- {
- QImage image = d->images[image_idx++];
- qreal x = d->reals[real_idx++];
- qreal y = d->reals[real_idx++];
- p->drawImage(QPointF(x, y), image);
- break;
- }
- default:
- break;
- }
- }
- d->clearCommands();
- }
-}
+ if (newState.shadowBlur != state.shadowBlur)
+ buffer()->setShadowBlur(newState.shadowBlur);
-QPaintDevice* QSGContext2D::paintDevice()
-{
- Q_D(QSGContext2D);
- return &d->cachedImage;
-}
-const QImage& QSGContext2D::toImage() const
-{
- Q_D(const QSGContext2D);
- return d->cachedImage;
-}
-bool QSGContext2D::requireCachedImage() const
-{
- Q_D(const QSGContext2D);
- return d->waitingForPainting;
-}
-void QSGContext2D::setCachedImage(const QImage& image)
-{
- Q_D(QSGContext2D);
-#ifndef QSGCANVASITEM_PAINTING_ON_IMAGE
- if (d->waitingForPainting) {
- d->cachedImage = image;
- d->waitingForPainting = false;
- }
-#endif
-}
+ if (newState.shadowColor != state.shadowColor)
+ buffer()->setShadowColor(newState.shadowColor);
-void QSGContext2D::clear()
-{
- Q_D(QSGContext2D);
- d->clear();
-}
+ if (newState.shadowOffsetX != state.shadowOffsetX)
+ buffer()->setShadowOffsetX(newState.shadowOffsetX);
-void QSGContext2D::reset()
-{
- Q_D(QSGContext2D);
- d->reset();
-}
+ if (newState.shadowOffsetY != state.shadowOffsetY)
+ buffer()->setShadowOffsetY(newState.shadowOffsetY);
-void QSGContext2D::drawImage(const QString& imgUrl, qreal dx, qreal dy)
-{
- Q_D(QSGContext2D);
- if (!imgUrl.isEmpty())
- d->drawImage(imgUrl, dx, dy);
+ state = newState;
}
-
-void QSGContext2D::drawImage(const QString& imgUrl, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh)
+void QSGContext2D::pushState()
{
- Q_D(QSGContext2D);
- if (!imgUrl.isEmpty())
- d->drawImage(imgUrl, sx, sy, sw, sh, dx, dy, dw, dh);
+ m_stateStack.push(state);
}
-void QSGContext2D::drawImage(const QString& imgUrl, qreal dx, qreal dy,
- qreal dw, qreal dh)
+void QSGContext2D::reset()
{
- Q_D(QSGContext2D);
- if (!imgUrl.isEmpty())
- d->drawImage(imgUrl, dx, dy, dw, dh);
+ QSGContext2D::State newState;
+ newState.matrix = QTransform();
+
+ QPainterPath defaultClipPath;
+ defaultClipPath.addRect(0, 0, m_canvas->canvasSize().width(), m_canvas->canvasSize().height());
+ newState.clipPath = defaultClipPath;
+ newState.clipPath.setFillRule(Qt::WindingFill);
+
+ newState.strokeStyle = Qt::black;
+ newState.fillStyle = Qt::black;
+ 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 = QSGContext2D::Start;
+ newState.textBaseline = QSGContext2D::Alphabetic;
+
+ m_fontString = "";
+ m_stateStack.clear();
+ m_stateStack.push(newState);
+ popState();
}
-void QSGContext2D::setSize(int width, int height)
+void QSGContext2D::setV8Engine(QV8Engine *engine)
{
- QSize size(width, height);
- setSize(size);
-}
+ v8::HandleScope handle_scope;
+ v8::Context::Scope scope(engine->context());
-void QSGContext2D::setSize(const QSize &size)
-{
- Q_D(QSGContext2D);
+ if (m_v8engine != engine) {
+ m_v8engine = engine;
- if (d->size == size)
- return;
- d->setSize(size);
- emit changed();
-}
+ qPersistentDispose(m_v8value);
-QSize QSGContext2D::size() const
-{
- Q_D(const QSGContext2D);
- return d->size;
-}
+ if (m_v8engine == 0)
+ return;
-QMatrix QSGContext2D::worldMatrix() const
-{
- Q_D(const QSGContext2D);
- return d->state.matrix;
+ QSGContext2DEngineData *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/declarative/items/context2d/qsgcontext2d_p.h b/src/declarative/items/context2d/qsgcontext2d_p.h
index f8fc9b75a2..830a185a31 100644
--- a/src/declarative/items/context2d/qsgcontext2d_p.h
+++ b/src/declarative/items/context2d/qsgcontext2d_p.h
@@ -45,324 +45,73 @@
#include <QtDeclarative/qdeclarative.h>
#include <QtDeclarative/qdeclarativecomponent.h>
-#include "qsgtexturematerial.h"
-
#include <QtGui/qpainter.h>
#include <QtGui/qpainterpath.h>
-#include <QtGui/qpixmap.h>
#include <QtCore/qstring.h>
#include <QtCore/qstack.h>
-#include <QtCore/qmetatype.h>
-#include <QtCore/qcoreevent.h>
-#include <QtCore/qvariant.h>
-#include <QtDeclarative/qjsvalue.h>
#include <private/qv8engine_p.h>
-#include <QMutex>
-#include <QWaitCondition>
-#include "qsgimage_p.h"
-
-QT_BEGIN_HEADER
-
-QT_BEGIN_NAMESPACE
-
-QT_MODULE(Declarative)
-
-QColor colorFromString(const QString &name);
-class QSGCanvasGradient : public QObject
-{
- Q_OBJECT
-public:
- QSGCanvasGradient(const QGradient &gradient) : m_gradient(gradient) {}
-public slots:
- QGradient value() { return m_gradient; }
- void addColorStop(float pos, const QString &color) { m_gradient.setColorAt(pos, colorFromString(color));}
-public:
- QGradient m_gradient;
-};
+#define QSGCONTEXT2D_DEBUG //enable this for just DEBUG purpose!
-Q_DECLARE_METATYPE(QSGCanvasGradient*)
+#ifdef QSGCONTEXT2D_DEBUG
+#include <QElapsedTimer>
+#endif
+QT_BEGIN_HEADER
-class QSGCanvasPath : QObject
-{
- Q_OBJECT
-public:
- QSGCanvasPath(const QPainterPath& path, QObject* parent = 0) : QObject(parent), m_path(path) {}
+QT_BEGIN_NAMESPACE
- QPainterPath m_path;
-};
-Q_DECLARE_METATYPE(QSGCanvasPath*)
+QT_MODULE(Declarative)
-class QSGContext2DWorkerAgent;
-class QSGContext2DPrivate;
class QSGCanvasItem;
-class QSGContext2D : public QObject
-{
- Q_OBJECT
- // compositing
- Q_PROPERTY(qreal globalAlpha READ globalAlpha WRITE setGlobalAlpha)
- Q_PROPERTY(QString globalCompositeOperation READ globalCompositeOperation WRITE setGlobalCompositeOperation)
- Q_PROPERTY(QVariant strokeStyle READ strokeStyle WRITE setStrokeStyle)
- Q_PROPERTY(QVariant fillStyle READ fillStyle WRITE setFillStyle)
- Q_PROPERTY(QColor strokeColor READ strokeColor WRITE setStrokeColor)
- Q_PROPERTY(QColor fillColor READ fillColor WRITE setFillColor)
- // line caps/joins
- Q_PROPERTY(qreal lineWidth READ lineWidth WRITE setLineWidth)
- Q_PROPERTY(QString lineCap READ lineCap WRITE setLineCap)
- Q_PROPERTY(QString lineJoin READ lineJoin WRITE setLineJoin)
- Q_PROPERTY(qreal miterLimit READ miterLimit WRITE setMiterLimit)
- // shadows
- Q_PROPERTY(qreal shadowOffsetX READ shadowOffsetX WRITE setShadowOffsetX)
- Q_PROPERTY(qreal shadowOffsetY READ shadowOffsetY WRITE setShadowOffsetY)
- Q_PROPERTY(qreal shadowBlur READ shadowBlur WRITE setShadowBlur)
- Q_PROPERTY(QString shadowColor READ shadowColor WRITE setShadowColor)
- // fonts
- Q_PROPERTY(QString font READ font WRITE setFont)
- Q_PROPERTY(QString textBaseline READ textBaseline WRITE setTextBaseline)
- Q_PROPERTY(QString textAlign READ textAlign WRITE setTextAlign)
+class QSGContext2DCommandBuffer;
+class QDeclarativePixmap;
- Q_PROPERTY(QSGCanvasPath* path READ path WRITE setPath)
- Q_ENUMS(PaintCommand)
+class QSGContext2D
+{
public:
enum TextBaseLineType { Alphabetic=0, Top, Middle, Bottom, Hanging};
enum TextAlignType { Start=0, End, Left, Right, Center};
enum PaintCommand {
Invalid = 0,
- Save,
- Restore,
- //matrix operations
UpdateMatrix,
- Scale,
- Rotate,
- Translate,
- Transform,
- SetTransform,
-
ClearRect,
FillRect,
-
- //path operations
- UpdatePath,
- BeginPath,
- ClosePath,
- MoveTo,
- LineTo,
- QuadraticCurveTo,
- BezierCurveTo,
- ArcTo,
- Rect,
- Arc,
+ StrokeRect,
Fill,
Stroke,
Clip,
- StrokeRect,
-
- //brushes and pens
UpdateBrush,
- UpdatePen,
GlobalAlpha,
GlobalCompositeOperation,
StrokeStyle,
FillStyle,
- StrokeColor,
- FillColor,
LineWidth,
LineCap,
LineJoin,
MiterLimit,
-
- //shadows
- UpdateShadow,
ShadowOffsetX,
ShadowOffsetY,
ShadowBlur,
ShadowColor,
-
- //font&text
Font,
TextBaseline,
TextAlign,
FillText,
StrokeText,
-
- //image
- DrawImage1,
- DrawImage2,
- DrawImage3,
- GetImageData,
- PutImageData
+ DrawImage,
+ GetImageData
};
- QSGContext2D(QObject *parent = 0);
- QSGContext2D(QSGContext2D *ctx2d, QSGContext2DWorkerAgent* agentData);
- ~QSGContext2D();
-
- QSGCanvasItem* canvas() const;
-
- void setSize(int width, int height);
- void setSize(const QSize &size);
- QSize size() const;
-
- void clear();
- void reset();
- QPaintDevice* paintDevice();
- const QImage& toImage() const;
- bool requireCachedImage() const;
- void setCachedImage(const QImage& image);
- // compositing
- qreal globalAlpha() const; // (default 1.0)
- QString globalCompositeOperation() const; // (default over)
- QVariant strokeStyle() const; // (default black)
- QVariant fillStyle() const; // (default black)
- QColor strokeColor() const; // (default black)
- QColor fillColor() const; // (default black)
-
- void setGlobalAlpha(qreal alpha);
- void setGlobalCompositeOperation(const QString &op);
- void setStrokeStyle(const QVariant &style);
- void setFillStyle(const QVariant &style);
- void setStrokeColor(const QColor& color);
- void setFillColor(const QColor& color);
-
- // line caps/joins
- qreal lineWidth() const; // (default 1)
- QString lineCap() const; // "butt", "round", "square" (default "butt")
- QString lineJoin() const; // "round", "bevel", "miter" (default "miter")
- qreal miterLimit() const; // (default 10)
-
- void setLineWidth(qreal w);
- void setLineCap(const QString &s);
- void setLineJoin(const QString &s);
- void setMiterLimit(qreal m);
-
- void setFont(const QString &font);
- QString font() const;
- void setTextBaseline(const QString &font);
- QString textBaseline() const;
- void setTextAlign(const QString &font);
- QString textAlign() const;
-
-
- // shadows
- qreal shadowOffsetX() const; // (default 0)
- qreal shadowOffsetY() const; // (default 0)
- qreal shadowBlur() const; // (default 0)
- QString shadowColor() const; // (default black)
-
- void setShadowOffsetX(qreal x);
- void setShadowOffsetY(qreal y);
- void setShadowBlur(qreal b);
- void setShadowColor(const QString &str);
-
- QSGCanvasPath* path();
- void setPath(QSGCanvasPath* path);
-public slots:
- void save(); // push state on state stack
- void restore(); // pop state stack and restore state
-
- // QTextMetrics measureText(const QString& text);
-
- void fillText(const QString &text, qreal x, qreal y);
- void strokeText(const QString &text, qreal x, qreal y);
-
- void scale(qreal x, qreal y);
- void rotate(qreal angle);
- void translate(qreal x, qreal y);
- void transform(qreal m11, qreal m12, qreal m21, qreal m22,
- qreal dx, qreal dy);
- void setTransform(qreal m11, qreal m12, qreal m21, qreal m22,
- qreal dx, qreal dy);
-
- QSGCanvasGradient *createLinearGradient(qreal x0, qreal y0,
- qreal x1, qreal y1);
- QSGCanvasGradient *createRadialGradient(qreal x0, qreal y0,
- qreal r0, qreal x1,
- qreal y1, qreal r1);
-
- // rects
- void clearRect(qreal x, qreal y, qreal w, qreal h);
- void fillRect(qreal x, qreal y, qreal w, qreal h);
- void strokeRect(qreal x, qreal y, qreal w, qreal h);
-
- // 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 arc(qreal x, qreal y, qreal radius,
- qreal startAngle, qreal endAngle,
- bool anticlockwise);
- void fill();
- void stroke();
- void clip();
- bool isPointInPath(qreal x, qreal y) const;
-
- //path string parser
- //implement the W3C SVG path spec:
- //http://www.w3.org/TR/SVG/paths.html
- void setPathString(const QString& path);
- QSGCanvasPath* createPath(const QString& pathString);
-
- QSGImage *createImage(const QString &url);
-
- void drawImage(const QString& imgUrl, qreal dx, qreal dy);
- void drawImage(const QString& imgUrl, qreal dx, qreal dy, qreal dw, qreal dh);
- void drawImage(const QString& imgUrl, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh);
-
- // pixel manipulation
- QList<int> getImageData(qreal sx, qreal sy, qreal sw, qreal sh);
- void putImageData(const QVariant& imageData, qreal x, qreal y, qreal w, qreal h);
-
- void paint(QPainter* painter);
- void sync();
- void processCommands(const QJSValue& commands);
-signals:
- void changed();
- void painted();
-public:
- bool isDirty() const;
- v8::Handle<v8::Object> v8value() const;
- QV8Engine* v8Engine() const;
- void setV8Engine(QV8Engine *eng);
-
- bool valid() const;
- void setValid(bool valid);
- void setTileRect(const QRectF& region);
- void addref();
- void release();
-
- struct VariantRef
- {
- VariantRef() : a(0) {}
- VariantRef(const VariantRef &r) : a(r.a) { if (a) a->addref(); }
- VariantRef(QSGContext2D *_a) : a(_a) { if (a) a->addref(); }
- ~VariantRef() { if (a) a->release(); }
-
- VariantRef &operator=(const VariantRef &o) {
- if (o.a) o.a->addref();
- if (a) a->release(); a = o.a;
- return *this;
- }
- QSGContext2D *a;
- };
- struct Sync : public QEvent {
- Sync() : QEvent(QEvent::User) {}
- QSGContext2DWorkerAgent *data;
- };
struct State {
- QMatrix matrix;
+ QTransform matrix;
QPainterPath clipPath;
QBrush strokeStyle;
QBrush fillStyle;
+ Qt::FillRule fillRule;
qreal globalAlpha;
qreal lineWidth;
Qt::PenCapStyle lineCap;
@@ -376,24 +125,58 @@ public:
QFont font;
QSGContext2D::TextAlignType textAlign;
QSGContext2D::TextBaseLineType textBaseline;
- QPen pen;
};
- QMatrix worldMatrix() const;
+ QSGContext2D(QSGCanvasItem* item);
+ ~QSGContext2D();
+
+ inline QSGCanvasItem* canvas() const {return m_canvas;}
+ inline QSGContext2DCommandBuffer* 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);
-protected:
- virtual bool event(QEvent *);
+ bool isPointInPath(qreal x, qreal y) const;
-private:
- void processCommand(const QJSValue& command);
+ QPainterPath createTextGlyphs(qreal x, qreal y, const QString& text);
+ QImage createImage(const QUrl& url);
- Q_DECLARE_PRIVATE(QSGContext2D)
+ State state;
+ QStack<QSGContext2D::State> m_stateStack;
+ QSGCanvasItem* m_canvas;
+ QSGContext2DCommandBuffer* m_buffer;
+ QPainterPath m_path;
+ v8::Local<v8::Value> m_fillStyle;
+ v8::Local<v8::Value> m_strokeStyle;
+ v8::Handle<v8::Value> m_v8path;
+ QString m_fontString;
+ QV8Engine *m_v8engine;
+ v8::Persistent<v8::Object> m_v8value;
};
QT_END_NAMESPACE
-
-Q_DECLARE_METATYPE(QSGContext2D::VariantRef)
QML_DECLARE_TYPE(QSGContext2D)
QT_END_HEADER
diff --git a/src/declarative/items/context2d/qsgcontext2d_p_p.h b/src/declarative/items/context2d/qsgcontext2d_p_p.h
deleted file mode 100644
index d71a0bccf2..0000000000
--- a/src/declarative/items/context2d/qsgcontext2d_p_p.h
+++ /dev/null
@@ -1,238 +0,0 @@
-/****************************************************************************
-**
-** 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$
-**
-****************************************************************************/
-
-#ifndef QSGCONTEXT2D_P_P_H
-#define QSGCONTEXT2D_P_P_H
-
-//
-// W A R N I N G
-// -------------
-//
-// This file is not part of the Qt API. It exists purely as an
-// implementation detail. This header file may change from version to
-// version without notice, or even be removed.
-//
-// We mean it.
-//
-
-#include "qsgcontext2d_p.h"
-#include <private/qobject_p.h>
-
-QT_BEGIN_NAMESPACE
-class QSGCanvasItem;
-struct QSGContext2DWorkerAgent {
- QSGContext2DWorkerAgent()
- :ref(1)
- , orig(0)
- {}
-
- QAtomicInt ref;
- QSGContext2D *orig;
- QMutex mutex;
- QWaitCondition syncDone;
-};
-
-class QSGContext2DPrivate : public QObjectPrivate
-{
- Q_DECLARE_PUBLIC(QSGContext2D)
-
-public:
- QSGContext2DPrivate()
- : agent(0)
- , agentData(0)
- , v8engine(0)
- , cachedImage(1,1, QImage::Format_ARGB32)
- , canvas(0)
- , waitingForPainting(false)
- , valid(false)
- {
- }
- ~QSGContext2DPrivate()
- {
- qPersistentDispose(v8value);
- }
-
- void updateMatrix(const QMatrix& m);
-
- void setSize(const QSize &s)
- {
- size = s;
- cachedImage = QImage(s, QImage::Format_ARGB32);
- }
- void clear();
- void reset();
-
- // compositing
- void setGlobalAlpha(qreal alpha);
- void setGlobalCompositeOperation(const QString &op);
- void setStrokeStyle(const QVariant &style);
- void setFillStyle(const QVariant &style);
- void setStrokeColor(const QColor& color);
- void setFillColor(const QColor& color);
-
- // line caps/joins
- void setLineWidth(qreal w);
- void setLineCap(const QString &s);
- void setLineJoin(const QString &s);
- void setMiterLimit(qreal m);
-
- void setFont(const QString &font);
- void setTextBaseline(const QString &font);
- void setTextAlign(const QString &font);
-
-
- // shadows
- void setShadowOffsetX(qreal x);
- void setShadowOffsetY(qreal y);
- void setShadowBlur(qreal b);
- void setShadowColor(const QString &str);
-
- bool hasShadow() const;
- void clearShadow();
- QImage makeShadowImage(const QPixmap& pix);
- void fillRectShadow(QPainter* p, QRectF shadowRect);
- void fillShadowPath(QPainter* p, const QPainterPath& path);
- void strokeShadowPath(QPainter* p, const QPainterPath& path);
- void save();
- void restore();
-
- // QTextMetrics measureText(const QString& text);
-
- void fillText(const QString &text, qreal x, qreal y);
- void strokeText(const QString &text, qreal x, qreal y);
-
- void scale(qreal x, qreal y);
- void rotate(qreal angle);
- void translate(qreal x, qreal y);
- void transform(qreal m11, qreal m12, qreal m21, qreal m22,
- qreal dx, qreal dy);
- void setTransform(qreal m11, qreal m12, qreal m21, qreal m22,
- qreal dx, qreal dy);
-
- // rects
- void clearRect(qreal x, qreal y, qreal w, qreal h);
- void fillRect(qreal x, qreal y, qreal w, qreal h);
- void strokeRect(qreal x, qreal y, qreal w, qreal h);
-
- // 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 arc(qreal x, qreal y, qreal radius,
- qreal startAngle, qreal endAngle,
- bool anticlockwise);
- void fill();
- void stroke();
- void clip();
-
- void drawImage(const QString& url, qreal dx, qreal dy);
- void drawImage(const QString& url, qreal dx, qreal dy, qreal dw, qreal dh);
- void drawImage(const QString& url, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh);
-
- QList<int> getImageData(qreal sx, qreal sy, qreal sw, qreal sh);
- void putImageData(const QVariantList& imageData, qreal x, qreal y, qreal w, qreal h);
-
- int baseLineOffset(QSGContext2D::TextBaseLineType value, const QFontMetrics &metrics);
- int textAlignOffset(QSGContext2D::TextAlignType value, const QFontMetrics &metrics, const QString &string);
-
- void clearCommands()
- {
- //qDebug() << "painting commands:" << commands.size();
- commands.remove(0, commands.size());
- variants.remove(0, variants.size());
- pens.remove(0, pens.size());
- ints.remove(0, ints.size());
- reals.remove(0, reals.size());
- strings.remove(0, strings.size());
- colors.remove(0, colors.size());
- matrixes.remove(0, matrixes.size());
- brushes.remove(0, brushes.size());
- pathes.remove(0, pathes.size());
- fonts.remove(0, fonts.size());
- images.remove(0, images.size());
- sizes.remove(0, sizes.size());
- }
-
- //current context2d variables
- QPainterPath path;
- QSize size;
- QSGContext2D::State state;
- QStack<QSGContext2D::State> stateStack;
-
- //variables for actual painting
- QVector<QSGContext2D::PaintCommand> commands;
- QVector<QVariant> variants;
- QVector<int> ints;
- QVector<qreal> reals;
- QVector<QString> strings;
- QVector<QColor> colors;
- QVector<QMatrix> matrixes;
- QVector<QPen> pens;
- QVector<QBrush> brushes;
- QVector<QPainterPath> pathes;
- QVector<QFont> fonts;
- QVector<QImage> images;
- QVector<QSize> sizes;
- QList<int> imageData;
-
- //workerscript agent
- QSGContext2D* agent;
- QSGContext2DWorkerAgent* agentData;
-
- QV8Engine *v8engine;
- v8::Persistent<v8::Object> v8value;
-
- QImage cachedImage;
- QSGCanvasItem* canvas;
- bool waitingForPainting;
- bool valid;
- QRectF tileRect;
-};
-
-QT_END_NAMESPACE
-
-#endif // QSGCONTEXT2D_P_P_H
diff --git a/src/declarative/items/context2d/qsgcontext2dcommandbuffer.cpp b/src/declarative/items/context2d/qsgcontext2dcommandbuffer.cpp
new file mode 100644
index 0000000000..8eb9513433
--- /dev/null
+++ b/src/declarative/items/context2d/qsgcontext2dcommandbuffer.cpp
@@ -0,0 +1,402 @@
+/****************************************************************************
+**
+** 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 "qsgcontext2dcommandbuffer_p.h"
+#include "qsgcanvasitem_p.h"
+#include "qdeclarative.h"
+#include <QtGui/QApplication>
+#include <QtCore/QMutex>
+
+#define HAS_SHADOW(offsetX, offsetY, blur, color) (color.isValid() && color.alpha() && (blur || offsetX || offsetY))
+static QImage makeShadowImage(const QImage& image, qreal offsetX, qreal offsetY, qreal blur, const QColor& color)
+{
+ QImage shadowImg(image.width() + blur * 2 + qAbs(offsetX),
+ image.height() + blur *2 + qAbs(offsetY),
+ QImage::Format_ARGB32);
+ 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();
+
+ // blur the alpha channel
+ if (blur > 0) {
+ QImage blurred(shadowImg.size(), QImage::Format_ARGB32);
+ blurred.fill(0);
+ QPainter blurPainter(&blurred);
+ qt_blurImage(&blurPainter, shadowImg, blur, true, false);
+ blurPainter.end();
+ shadowImg = blurred;
+ }
+
+ // 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);
+ 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);
+ 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);
+ 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());
+}
+
+QPen QSGContext2DCommandBuffer::makePen(QSGContext2D::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 QSGContext2DCommandBuffer::setPainterState(QPainter* p, QSGContext2D::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);
+}
+
+QSGContext2D::State QSGContext2DCommandBuffer::replay(QPainter* p, QSGContext2D::State state)
+{
+ if (!p)
+ return state;
+
+ reset();
+
+ QTransform originMatrix = p->transform();
+
+ QPen pen = makePen(state);
+ setPainterState(p, state, pen);
+
+ while (hasNext()) {
+ QSGContext2D::PaintCommand cmd = takeNextCommand();
+ switch (cmd) {
+ case QSGContext2D::UpdateMatrix:
+ {
+ state.matrix = takeMatrix();
+ p->setTransform(state.matrix * originMatrix);
+ break;
+ }
+ case QSGContext2D::ClearRect:
+ {
+ p->eraseRect(takeRect());
+ break;
+ }
+ case QSGContext2D::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 QSGContext2D::ShadowColor:
+ {
+ state.shadowColor = takeColor();
+ break;
+ }
+ case QSGContext2D::ShadowBlur:
+ {
+ state.shadowBlur = takeShadowBlur();
+ break;
+ }
+ case QSGContext2D::ShadowOffsetX:
+ {
+ state.shadowOffsetX = takeShadowOffsetX();
+ break;
+ }
+ case QSGContext2D::ShadowOffsetY:
+ {
+ state.shadowOffsetY = takeShadowOffsetY();
+ break;
+ }
+ case QSGContext2D::FillStyle:
+ {
+ state.fillStyle = takeFillStyle();
+ p->setBrush(state.fillStyle);
+ break;
+ }
+ case QSGContext2D::StrokeStyle:
+ {
+ state.strokeStyle = takeStrokeStyle();
+ pen.setBrush(state.strokeStyle);
+ p->setPen(pen);
+ break;
+ }
+ case QSGContext2D::LineWidth:
+ {
+ state.lineWidth = takeLineWidth();
+ pen.setWidth(state.lineWidth);
+ p->setPen(pen);
+ break;
+ }
+ case QSGContext2D::LineCap:
+ {
+ state.lineCap = takeLineCap();
+ pen.setCapStyle(state.lineCap);
+ p->setPen(pen);
+ break;
+ }
+ case QSGContext2D::LineJoin:
+ {
+ state.lineJoin = takeLineJoin();
+ pen.setJoinStyle(state.lineJoin);
+ p->setPen(pen);
+ break;
+ }
+ case QSGContext2D::MiterLimit:
+ {
+ state.miterLimit = takeMiterLimit();
+ pen.setMiterLimit(state.miterLimit);
+ p->setPen(pen);
+ break;
+ }
+ case QSGContext2D::TextAlign:
+ case QSGContext2D::TextBaseline:
+ break;
+ case QSGContext2D::Fill:
+ {
+ if (HAS_SHADOW(state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor))
+ fillShadowPath(p,takePath(), state.shadowOffsetX, state.shadowOffsetY, state.shadowBlur, state.shadowColor);
+ else
+ p->fillPath(takePath(), p->brush());
+ break;
+ }
+ case QSGContext2D::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 QSGContext2D::Clip:
+ {
+ QPainterPath clipPath = takePath();
+ clipPath.closeSubpath();
+ state.clipPath = state.clipPath.intersected(clipPath);
+ if (!p->clipPath().isEmpty())
+ clipPath = clipPath.intersected(p->clipPath());
+ p->setClipping(true);
+ p->setClipPath(clipPath);
+ break;
+ }
+ case QSGContext2D::UpdateBrush:
+ {
+ state.fillStyle = takeBrush();
+ p->setBrush(state.fillStyle);
+ break;
+ }
+
+ case QSGContext2D::GlobalAlpha:
+ {
+ state.globalAlpha = takeGlobalAlpha();
+ p->setOpacity(state.globalAlpha);
+ break;
+ }
+ case QSGContext2D::GlobalCompositeOperation:
+ {
+ state.globalCompositeOperation = takeGlobalCompositeOperation();
+ p->setCompositionMode(state.globalCompositeOperation);
+ break;
+ }
+ case QSGContext2D::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 QSGContext2D::GetImageData:
+ {
+ //TODO:
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ p->end();
+ return state;
+}
+
+QSGContext2DCommandBuffer::QSGContext2DCommandBuffer()
+ : cmdIdx(0)
+ , intIdx(0)
+ , realIdx(0)
+ , colorIdx(0)
+ , matrixIdx(0)
+ , brushIdx(0)
+ , pathIdx(0)
+ , imageIdx(0)
+{
+}
+
+
+QSGContext2DCommandBuffer::~QSGContext2DCommandBuffer()
+{
+}
+
+void QSGContext2DCommandBuffer::clear()
+{
+ commands.clear();
+ ints.clear();
+ reals.clear();
+ colors.clear();
+ matrixes.clear();
+ brushes.clear();
+ pathes.clear();
+ images.clear();
+ reset();
+}
+
+void QSGContext2DCommandBuffer::reset()
+{
+ cmdIdx = 0;
+ intIdx = 0;
+ realIdx = 0;
+ colorIdx = 0;
+ matrixIdx = 0;
+ brushIdx = 0;
+ pathIdx = 0;
+ imageIdx = 0;
+}
+
+
diff --git a/src/declarative/items/context2d/qsgcontext2dcommandbuffer_p.h b/src/declarative/items/context2d/qsgcontext2dcommandbuffer_p.h
new file mode 100644
index 0000000000..d238027c05
--- /dev/null
+++ b/src/declarative/items/context2d/qsgcontext2dcommandbuffer_p.h
@@ -0,0 +1,268 @@
+/****************************************************************************
+**
+** 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 QSGCONTEXT2DCOMMANDBUFFER_P_H
+#define QSGCONTEXT2DCOMMANDBUFFER_P_H
+
+#include "qsgcontext2d_p.h"
+#include "qdeclarativepixmapcache_p.h"
+
+// Note, this is exported but in a private header as qtopengl depends on it.
+// But it really should be considered private API
+void qt_blurImage(QPainter *p, QImage &blurImage, qreal radius, bool quality, bool alphaOnly, int transposed = 0);
+void qt_blurImage(QImage &blurImage, qreal radius, bool quality, int transposed = 0);
+
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGCanvasItem;
+class QMutex;
+
+class QSGContext2DCommandBuffer
+{
+public:
+ QSGContext2DCommandBuffer();
+ ~QSGContext2DCommandBuffer();
+ 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 QSGContext2D::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 << QSGContext2D::GlobalAlpha;
+ reals << alpha;
+ }
+
+ inline void setGlobalCompositeOperation(QPainter::CompositionMode cm)
+ {
+ commands << QSGContext2D::GlobalCompositeOperation;
+ ints << cm;
+ }
+
+ inline void setStrokeStyle(const QBrush &style)
+ {
+ commands << QSGContext2D::StrokeStyle;
+ brushes << style;
+ }
+
+ inline void drawImage(const QImage& image, qreal sx, qreal sy, qreal sw, qreal sh, qreal dx, qreal dy, qreal dw, qreal dh)
+ {
+ commands << QSGContext2D::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 << QSGContext2D::UpdateMatrix;
+ matrixes << matrix;
+ }
+
+ inline void clearRect(qreal x, qreal y, qreal w, qreal h)
+ {
+ commands << QSGContext2D::ClearRect;
+ reals << x << y << w << h;
+ }
+
+ inline void fillRect(qreal x, qreal y, qreal w, qreal h)
+ {
+ commands << QSGContext2D::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 << QSGContext2D::Stroke;
+ pathes << p;
+ }
+
+
+ inline void fill(const QPainterPath& path)
+ {
+ commands << QSGContext2D::Fill;
+ pathes << path;
+
+ }
+
+ inline void stroke(const QPainterPath& path)
+ {
+ commands << QSGContext2D::Stroke;
+ pathes << path;
+ }
+
+ inline void clip(const QPainterPath& path)
+ {
+ commands << QSGContext2D::Clip;
+ pathes << path;
+ }
+
+
+
+ inline void setFillStyle(const QBrush &style)
+ {
+ commands << QSGContext2D::UpdateBrush;
+ brushes << style;
+ }
+
+
+ inline void setLineWidth( qreal w)
+ {
+ commands << QSGContext2D::LineWidth;
+ reals << w;
+ }
+
+ inline void setLineCap(Qt::PenCapStyle cap)
+ {
+ commands << QSGContext2D::LineCap;
+ ints << cap;
+ }
+
+ inline void setLineJoin(Qt::PenJoinStyle join)
+ {
+ commands << QSGContext2D::LineJoin;
+ ints << join;
+ }
+
+ inline void setMiterLimit( qreal limit)
+ {
+ commands << QSGContext2D::MiterLimit;
+ reals << limit;
+ }
+
+ inline void setShadowOffsetX( qreal x)
+ {
+ commands << QSGContext2D::ShadowOffsetX;
+ reals << x;
+ }
+
+ inline void setShadowOffsetY( qreal y)
+ {
+ commands << QSGContext2D::ShadowOffsetY;
+ reals << y;
+ }
+
+ inline void setShadowBlur( qreal b)
+ {
+ commands << QSGContext2D::ShadowBlur;
+ reals << b;
+ }
+
+ inline void setShadowColor(const QColor &color)
+ {
+ commands << QSGContext2D::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 qreal takeReal() { return reals[realIdx++]; }
+ inline QColor takeColor() { return colors[colorIdx++]; }
+ inline QBrush takeBrush() { return brushes[brushIdx++]; }
+
+ QSGContext2D::State replay(QPainter* painter, QSGContext2D::State state);
+private:
+ QPen makePen(QSGContext2D::State state);
+ void setPainterState(QPainter* painter, QSGContext2D::State state, const QPen& pen);
+ int cmdIdx;
+ int intIdx;
+ int realIdx;
+ int colorIdx;
+ int matrixIdx;
+ int brushIdx;
+ int pathIdx;
+ int imageIdx;
+ QVector<QSGContext2D::PaintCommand> commands;
+
+ QVector<int> ints;
+ 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 // QSGCONTEXT2DCOMMANDBUFFER_P_H
diff --git a/src/declarative/items/context2d/qsgcontext2dnode.cpp b/src/declarative/items/context2d/qsgcontext2dnode.cpp
new file mode 100644
index 0000000000..4173b32e60
--- /dev/null
+++ b/src/declarative/items/context2d/qsgcontext2dnode.cpp
@@ -0,0 +1,116 @@
+/****************************************************************************
+**
+** 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 "qsgcontext2dnode_p.h"
+
+#include <private/qsgcontext_p.h>
+#include <QtCore/qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+
+QSGContext2DNode::QSGContext2DNode(QSGCanvasItem* item)
+ : QSGGeometryNode()
+ , m_item(item)
+ , m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4)
+ , m_texture(0)
+ , m_dirtyGeometry(false)
+ , m_dirtyTexture(false)
+{
+ setMaterial(&m_materialO);
+ setOpaqueMaterial(&m_material);
+ setGeometry(&m_geometry);
+ setFlag(UsePreprocess, true);
+}
+
+QSGContext2DNode::~QSGContext2DNode()
+{
+}
+
+void QSGContext2DNode::preprocess()
+{
+ bool doDirty = false;
+ QSGDynamicTexture *t = qobject_cast<QSGDynamicTexture *>(m_material.texture());
+ if (t) {
+ doDirty = t->updateTexture();
+ updateGeometry();
+ }
+ if (doDirty) {
+ m_dirtyTexture = true;
+ markDirty(DirtyMaterial);
+ }
+}
+void QSGContext2DNode::setTexture(QSGContext2DTexture* texture)
+{
+ if (texture != m_texture) {
+ m_dirtyTexture = true;
+ m_texture = texture;
+ }
+}
+
+void QSGContext2DNode::update()
+{
+ if (m_dirtyGeometry)
+ updateGeometry();
+ if (m_dirtyTexture)
+ updateTexture();
+
+ m_dirtyGeometry = false;
+ m_dirtyTexture = false;
+}
+
+void QSGContext2DNode::updateTexture()
+{
+ m_material.setTexture(m_texture);
+ m_materialO.setTexture(m_texture);
+ markDirty(DirtyMaterial);
+}
+
+void QSGContext2DNode::updateGeometry()
+{
+ QSizeF size = m_item->canvasWindow().size();
+ QRectF source = m_texture->textureSubRect();
+ QSGGeometry::updateTexturedRectGeometry(&m_geometry,
+ QRectF(0, 0, size.width(), size.height()),
+ source);
+ markDirty(DirtyGeometry);
+}
+QT_END_NAMESPACE
diff --git a/src/declarative/items/context2d/qsgcontext2dnode_p.h b/src/declarative/items/context2d/qsgcontext2dnode_p.h
new file mode 100644
index 0000000000..70446d7357
--- /dev/null
+++ b/src/declarative/items/context2d/qsgcontext2dnode_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 QSGCONTEXT2DNODE_P_H
+#define QSGCONTEXT2DNODE_P_H
+
+#include "qsgnode.h"
+#include "qsgtexturematerial.h"
+
+#include "qsgcanvasitem_p.h"
+#include "qsgcontext2dtexture_p.h"
+#include "qsgcontext2d_p.h"
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGContext2DNode : public QSGGeometryNode
+{
+public:
+ QSGContext2DNode(QSGCanvasItem* item);
+ virtual ~QSGContext2DNode();
+ void setTexture(QSGContext2DTexture* texture);
+ void update();
+ void preprocess();
+private:
+ void updateTexture();
+ void updateGeometry();
+
+ QSGCanvasItem* m_item;
+ QSGOpaqueTextureMaterial m_material;
+ QSGTextureMaterial m_materialO;
+ QSGGeometry m_geometry;
+ QSGContext2DTexture* m_texture;
+
+ bool m_dirtyGeometry;
+ bool m_dirtyTexture;
+};
+
+QT_END_HEADER
+
+QT_END_NAMESPACE
+
+#endif // QSGCONTEXT2DNODE_P_H
diff --git a/src/declarative/items/context2d/qsgcontext2dtexture.cpp b/src/declarative/items/context2d/qsgcontext2dtexture.cpp
new file mode 100644
index 0000000000..6e2c7c3e3c
--- /dev/null
+++ b/src/declarative/items/context2d/qsgcontext2dtexture.cpp
@@ -0,0 +1,673 @@
+#include "qsgcontext2dtexture_p.h"
+#include "qsgcontext2dtile_p.h"
+#include "qsgcanvasitem_p.h"
+#include "qsgitem_p.h"
+#include "private/qsgtexture_p.h"
+#include "qsgcontext2dcommandbuffer_p.h"
+
+#include <QtOpenGL/QGLFramebufferObject>
+#include <QtOpenGL/QGLFramebufferObjectFormat>
+#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)
+
+
+QSGContext2DTexture::QSGContext2DTexture()
+ : QSGDynamicTexture()
+ , m_context(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)
+{
+}
+
+QSGContext2DTexture::~QSGContext2DTexture()
+{
+ clearTiles();
+}
+
+QSize QSGContext2DTexture::textureSize() const
+{
+ return m_canvasWindow.size();
+}
+
+void QSGContext2DTexture::markDirtyTexture()
+{
+ lock();
+ m_dirtyTexture = true;
+ unlock();
+ emit textureChanged();
+}
+
+bool QSGContext2DTexture::setCanvasSize(const QSize &size)
+{
+ if (m_canvasSize != size) {
+ m_canvasSize = size;
+ m_dirtyCanvas = true;
+ return true;
+ }
+ return false;
+}
+
+bool QSGContext2DTexture::setTileSize(const QSize &size)
+{
+ if (m_tileSize != size) {
+ m_tileSize = size;
+ m_dirtyCanvas = true;
+ return true;
+ }
+ return false;
+}
+
+void QSGContext2DTexture::setSmooth(bool smooth)
+{
+ m_smooth = smooth;
+}
+
+void QSGContext2DTexture::setItem(QSGCanvasItem* 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()), Qt::QueuedConnection);
+ canvasChanged(item->canvasSize().toSize()
+ , item->tileSize()
+ , item->canvasWindow().toAlignedRect()
+ , item->canvasWindow().toAlignedRect()
+ , item->smooth());
+ }
+}
+
+bool QSGContext2DTexture::setCanvasWindow(const QRect& r)
+{
+ if (m_canvasWindow != r) {
+ m_canvasWindow = r;
+ }
+}
+
+bool QSGContext2DTexture::setDirtyRect(const QRect &r)
+{
+ bool doDirty = false;
+ foreach (QSGContext2DTile* t, m_tiles) {
+ bool dirty = t->rect().intersected(r).isValid();
+ t->markDirty(dirty);
+ if (dirty)
+ doDirty = true;
+ }
+ return doDirty;
+}
+
+void QSGContext2DTexture::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());
+
+ bool canvasChanged = setCanvasSize(canvasSize);
+ bool tileChanged = setTileSize(ts);
+
+ bool doDirty = false;
+ if (canvasSize == canvasWindow.size()) {
+ m_tiledCanvas = false;
+ m_dirtyCanvas = false;
+ } else {
+ m_tiledCanvas = true;
+ 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 QSGContext2DTexture::paintWithoutTiles()
+{
+ QSGContext2DCommandBuffer* 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);
+ m_state = ccb->replay(&p, m_state);
+
+ ccb->clear();
+ markDirtyTexture();
+ endPainting();
+}
+
+bool QSGContext2DTexture::canvasDestroyed()
+{
+ bool noCanvas = false;
+ lock();
+ noCanvas = m_item == 0;
+ unlock();
+ return noCanvas;
+}
+
+void QSGContext2DTexture::paint()
+{
+ if (canvasDestroyed())
+ return;
+
+ if (!m_tiledCanvas) {
+ paintWithoutTiles();
+ } else {
+ QSGContext2D::State oldState = m_state;
+ QSGContext2DCommandBuffer* ccb = m_context->buffer();
+
+ 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 (QSGContext2DTile* 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()) {
+ foreach (QSGContext2DTile* 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) {
+ m_state = ccb->replay(tile->createPainter(smooth), oldState);
+
+ lock();
+ tile->markDirty(false);
+ unlock();
+ }
+
+ compositeTile(tile);
+ }
+ ccb->clear();
+ endPainting();
+ markDirtyTexture();
+ }
+ }
+ }
+}
+
+QRect QSGContext2DTexture::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 QSGContext2DTexture::createTiles(const QRect& window)
+{
+ QList<QSGContext2DTile*> 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;
+
+ QSGContext2DTile* 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 QSGContext2DTexture::clearTiles()
+{
+ qDeleteAll(m_tiles);
+ m_tiles.clear();
+}
+
+QSGContext2DFBOTexture::QSGContext2DFBOTexture()
+ : QSGContext2DTexture()
+ , m_fbo(0)
+{
+ m_threadRendering = false;
+}
+
+bool QSGContext2DFBOTexture::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 QSGContext2DFBOTexture::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 QSGContext2DFBOTexture::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 QSGContext2DFBOTexture::bind()
+{
+ glBindTexture(GL_TEXTURE_2D, textureId());
+ updateBindOptions();
+}
+
+QRectF QSGContext2DFBOTexture::textureSubRect() const
+{
+ return QRectF(0
+ , 1
+ , qreal(m_canvasWindow.width()) / m_fboSize.width()
+ , qreal(-m_canvasWindow.height()) / m_fboSize.height());
+}
+
+
+int QSGContext2DFBOTexture::textureId() const
+{
+ return m_fbo? m_fbo->texture() : 0;
+}
+
+
+bool QSGContext2DFBOTexture::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;
+}
+
+QSGContext2DTile* QSGContext2DFBOTexture::createTile() const
+{
+ return new QSGContext2DFBOTile();
+}
+
+void QSGContext2DFBOTexture::grabImage()
+{
+ if (m_fbo) {
+ m_grabedImage = m_fbo->toImage();
+ }
+}
+
+QImage QSGContext2DFBOTexture::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 QSGContext2DFBOTexture::compositeTile(QSGContext2DTile* tile)
+{
+ QSGContext2DFBOTile* t = static_cast<QSGContext2DFBOTile*>(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());
+
+ QGLFramebufferObject::blitFramebuffer(m_fbo, target, t->fbo(), source);
+ }
+}
+QSGCanvasItem::RenderTarget QSGContext2DFBOTexture::renderTarget() const
+{
+ return QSGCanvasItem::FramebufferObject;
+}
+QPaintDevice* QSGContext2DFBOTexture::beginPainting()
+{
+ QSGContext2DTexture::beginPainting();
+
+ if (m_canvasWindow.size().isEmpty() && !m_threadRendering) {
+ delete m_fbo;
+ m_fbo = 0;
+ } else if (!m_fbo || m_fbo->size() != m_fboSize) {
+ QGLFramebufferObjectFormat format;
+ format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+ format.setInternalTextureFormat(GL_RGBA);
+ format.setMipmap(false);
+ format.setTextureTarget(GL_TEXTURE_2D);
+ delete m_fbo;
+ glDisable(GL_DEPTH_TEST);
+ glDepthMask(false);
+
+ m_fbo = new QGLFramebufferObject(m_fboSize, format);
+ glBindTexture(GL_TEXTURE_2D, m_fbo->texture());
+ updateBindOptions(false);
+ }
+ return m_fbo;
+}
+
+void qt_quit_context2d_render_thread()
+{
+ QThread* thread = globalCanvasThreadRenderInstance();
+ thread->quit();
+ thread->wait();
+}
+
+QSGContext2DImageTexture::QSGContext2DImageTexture(bool threadRendering)
+ : QSGContext2DTexture()
+ , 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();
+ }
+ }
+}
+
+QSGContext2DImageTexture::~QSGContext2DImageTexture()
+{
+ m_texture->deleteLater();
+}
+
+int QSGContext2DImageTexture::textureId() const
+{
+ return m_texture->textureId();
+}
+
+void QSGContext2DImageTexture::lock()
+{
+ if (m_threadRendering)
+ m_mutex.lock();
+}
+void QSGContext2DImageTexture::unlock()
+{
+ if (m_threadRendering)
+ m_mutex.unlock();
+}
+
+void QSGContext2DImageTexture::wait()
+{
+ if (m_threadRendering)
+ m_waitCondition.wait(&m_mutex);
+}
+
+void QSGContext2DImageTexture::wake()
+{
+ if (m_threadRendering)
+ m_waitCondition.wakeOne();
+}
+
+bool QSGContext2DImageTexture::supportDirectRendering() const
+{
+ return !m_threadRendering;
+}
+
+QSGCanvasItem::RenderTarget QSGContext2DImageTexture::renderTarget() const
+{
+ return QSGCanvasItem::Image;
+}
+
+void QSGContext2DImageTexture::bind()
+{
+ m_texture->bind();
+}
+
+bool QSGContext2DImageTexture::updateTexture()
+{
+ lock();
+ bool textureUpdated = m_dirtyTexture;
+ if (m_dirtyTexture) {
+ m_texture->setImage(m_image);
+ m_dirtyTexture = false;
+ }
+ unlock();
+ return textureUpdated;
+}
+
+QSGContext2DTile* QSGContext2DImageTexture::createTile() const
+{
+ return new QSGContext2DImageTile();
+}
+
+void QSGContext2DImageTexture::grabImage(const QRect& r)
+{
+ m_doGrabImage = true;
+ paint();
+ m_doGrabImage = false;
+ m_grabedImage = m_image.copy(r);
+}
+
+QImage QSGContext2DImageTexture::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* QSGContext2DImageTexture::beginPainting()
+{
+ QSGContext2DTexture::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(Qt::transparent);
+ }
+ unlock();
+ return &m_image;
+}
+
+void QSGContext2DImageTexture::compositeTile(QSGContext2DTile* tile)
+{
+ Q_ASSERT(!tile->dirty());
+ QSGContext2DImageTile* t = static_cast<QSGContext2DImageTile*>(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/declarative/items/context2d/qsgcontext2dtexture_p.h b/src/declarative/items/context2d/qsgcontext2dtexture_p.h
new file mode 100644
index 0000000000..50e5be774d
--- /dev/null
+++ b/src/declarative/items/context2d/qsgcontext2dtexture_p.h
@@ -0,0 +1,200 @@
+/****************************************************************************
+**
+** 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 QSGCONTEXT2DTEXTURE_P_H
+#define QSGCONTEXT2DTEXTURE_P_H
+
+#include "qsgtexture.h"
+#include "qsgcanvasitem_p.h"
+#include "qsgcontext2d_p.h"
+
+#include <QtOpenGL/QGLContext>
+#include <QtOpenGL/QGLFramebufferObject>
+
+#include <QtCore/QMutex>
+#include <QtCore/QWaitCondition>
+#include <QtCore/QThread>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGContext2DTile;
+class QSGContext2DCommandBuffer;
+
+class QSGContext2DTexture : public QSGDynamicTexture
+{
+ Q_OBJECT
+public:
+ QSGContext2DTexture();
+ ~QSGContext2DTexture();
+
+ 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 QSGCanvasItem::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(QSGCanvasItem* item);
+ void paint();
+
+protected:
+ void paintWithoutTiles();
+ virtual QPaintDevice* beginPainting() {m_painting = true;}
+ virtual void endPainting() {m_painting = false;}
+ virtual QSGContext2DTile* createTile() const = 0;
+ virtual void compositeTile(QSGContext2DTile* tile) = 0;
+
+ void clearTiles();
+ QRect createTiles(const QRect& window);
+
+ QList<QSGContext2DTile*> m_tiles;
+ QSGContext2D* m_context;
+
+ QSGContext2D::State m_state;
+
+ QSGCanvasItem* 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 QSGContext2DFBOTexture : public QSGContext2DTexture
+{
+ Q_OBJECT
+
+public:
+ QSGContext2DFBOTexture();
+ virtual int textureId() const;
+ virtual bool updateTexture();
+ virtual QSGContext2DTile* createTile() const;
+ virtual QImage toImage(const QRectF& region = QRectF());
+ virtual QPaintDevice* beginPainting();
+ QRectF textureSubRect() const;
+ virtual bool supportThreadRendering() const {return false;}
+ virtual bool supportDirectRendering() const {return false;}
+ virtual QSGCanvasItem::RenderTarget renderTarget() const;
+ virtual void compositeTile(QSGContext2DTile* 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:
+ QImage m_grabedImage;
+ QGLFramebufferObject *m_fbo;
+ QMutex m_mutex;
+ QWaitCondition m_condition;
+ QSize m_fboSize;
+};
+
+class QSGPlainTexture;
+class QSGContext2DImageTexture : public QSGContext2DTexture
+{
+ Q_OBJECT
+
+public:
+ QSGContext2DImageTexture(bool threadRendering = true);
+ ~QSGContext2DImageTexture();
+ virtual int textureId() const;
+ virtual void bind();
+ virtual bool supportThreadRendering() const {return true;}
+ virtual bool supportDirectRendering() const;
+ virtual QSGCanvasItem::RenderTarget renderTarget() const;
+ virtual void lock();
+ virtual void unlock();
+ virtual void wait();
+ virtual void wake();
+
+ virtual bool updateTexture();
+ virtual QSGContext2DTile* createTile() const;
+ virtual QImage toImage(const QRectF& region = QRectF());
+ virtual QPaintDevice* beginPainting();
+ virtual void compositeTile(QSGContext2DTile* 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 // QSGCONTEXT2DTEXTURE_P_H
diff --git a/src/declarative/items/context2d/qsgcontext2dtile.cpp b/src/declarative/items/context2d/qsgcontext2dtile.cpp
new file mode 100644
index 0000000000..695c30f211
--- /dev/null
+++ b/src/declarative/items/context2d/qsgcontext2dtile.cpp
@@ -0,0 +1,146 @@
+/****************************************************************************
+**
+** 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 "qsgcontext2dtile_p.h"
+
+#include <QtOpenGL/QGLFramebufferObject>
+#include <QtOpenGL/QGLFramebufferObjectFormat>
+
+QSGContext2DTile::QSGContext2DTile()
+ : m_dirty(true)
+ , m_rect(QRect(0, 0, 1, 1))
+ , m_device(0)
+{
+}
+
+QSGContext2DTile::~QSGContext2DTile()
+{
+ if (m_painter.isActive())
+ m_painter.end();
+}
+
+QPainter* QSGContext2DTile::createPainter(bool smooth)
+{
+ if (m_painter.isActive())
+ m_painter.end();
+
+ if (m_device) {
+ m_painter.begin(m_device);
+ m_painter.resetTransform();
+ m_painter.setCompositionMode(QPainter::CompositionMode_Source);
+
+#ifdef QSGCONTEXT2D_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;
+}
+
+QSGContext2DFBOTile::QSGContext2DFBOTile()
+ : QSGContext2DTile()
+ , m_fbo(0)
+{
+}
+
+QSGContext2DFBOTile::~QSGContext2DFBOTile()
+{
+ delete m_fbo;
+}
+
+void QSGContext2DFBOTile::setRect(const QRect& r)
+{
+ if (m_rect == r)
+ return;
+ m_rect = r;
+ m_dirty = true;
+ if (!m_fbo || m_fbo->size() != r.size()) {
+ QGLFramebufferObjectFormat format;
+ format.setAttachment(QGLFramebufferObject::CombinedDepthStencil);
+ format.setInternalTextureFormat(GL_RGBA);
+ format.setMipmap(false);
+
+ if (m_painter.isActive())
+ m_painter.end();
+
+ delete m_fbo;
+ m_fbo = new QGLFramebufferObject(r.size(), format);
+ }
+ m_device = m_fbo;
+}
+
+
+QSGContext2DImageTile::QSGContext2DImageTile()
+ : QSGContext2DTile()
+{
+}
+
+QSGContext2DImageTile::~QSGContext2DImageTile()
+{
+}
+
+void QSGContext2DImageTile::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/declarative/items/context2d/qsgcontext2dtile_p.h b/src/declarative/items/context2d/qsgcontext2dtile_p.h
new file mode 100644
index 0000000000..cee2125f73
--- /dev/null
+++ b/src/declarative/items/context2d/qsgcontext2dtile_p.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** 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 QSGCONTEXT2DTILE_P_H
+#define QSGCONTEXT2DTILE_P_H
+
+#include "qsgcontext2d_p.h"
+#include <QtOpenGL/QGLFramebufferObject>
+
+QT_BEGIN_HEADER
+
+QT_BEGIN_NAMESPACE
+
+QT_MODULE(Declarative)
+
+class QSGContext2DTexture;
+class QSGContext2DCommandBuffer;
+
+class QSGContext2DTile
+{
+public:
+ QSGContext2DTile();
+ ~QSGContext2DTile();
+
+ 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);
+
+protected:
+ uint m_dirty : 1;
+ QRect m_rect;
+ QPaintDevice* m_device;
+ QPainter m_painter;
+};
+
+
+class QSGContext2DFBOTile : public QSGContext2DTile
+{
+public:
+ QSGContext2DFBOTile();
+ ~QSGContext2DFBOTile();
+ virtual void setRect(const QRect& r);
+ QGLFramebufferObject* fbo() const {return m_fbo;}
+private:
+ QGLFramebufferObject *m_fbo;
+};
+
+class QSGContext2DImageTile : public QSGContext2DTile
+{
+public:
+ QSGContext2DImageTile();
+ ~QSGContext2DImageTile();
+ void setRect(const QRect& r);
+ const QImage& image() const {return m_image;}
+private:
+ QImage m_image;
+};
+QT_END_HEADER
+
+QT_END_NAMESPACE
+
+#endif // QSGCONTEXT2DTILE_P_H