From bfb3c424abc7c839dc44de0e36024ede99ece736 Mon Sep 17 00:00:00 2001 From: Kent Hansen Date: Fri, 14 Oct 2011 10:51:42 +0200 Subject: Rename Qt Quick-specific classes to QQuick* The QSG (SceneGraph) prefix is too generic for Qt Quick(2)-specific classes. All the classes and files in the declarative/items directory have been renamed. In particular, for classes that are currently public, the renaming is as follows: QSGView --> QQuickView QSGCanvas --> QQuickCanvas QSGItem --> QQuickItem QSGPaintedItem --> QQuickPaintedItem The header files have been renamed accordingly (e.g. qsgview.h --> qquickview.h). Change-Id: Iac937fff81db20bb639486a793c3aeb5230b038c Reviewed-by: Kent Hansen --- .../items/context2d/qquickcanvasitem.cpp | 728 +++++++++++++++++++++ 1 file changed, 728 insertions(+) create mode 100644 src/declarative/items/context2d/qquickcanvasitem.cpp (limited to 'src/declarative/items/context2d/qquickcanvasitem.cpp') diff --git a/src/declarative/items/context2d/qquickcanvasitem.cpp b/src/declarative/items/context2d/qquickcanvasitem.cpp new file mode 100644 index 0000000000..86db5e616c --- /dev/null +++ b/src/declarative/items/context2d/qquickcanvasitem.cpp @@ -0,0 +1,728 @@ +/**************************************************************************** +** +** 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 +#include "qquickcanvasitem_p.h" +#include +#include "qquickcontext2d_p.h" +#include "qquickcontext2dnode_p.h" +#include "qquickcontext2dtexture_p.h" +#include + +#include +#include +#include + +QT_BEGIN_NAMESPACE + +class QQuickCanvasItemPrivate : public QQuickItemPrivate +{ +public: + QQuickCanvasItemPrivate(); + ~QQuickCanvasItemPrivate(); + QQuickContext2D* context; + QQuickContext2DTexture* texture; + QSizeF canvasSize; + QSize tileSize; + QRectF canvasWindow; + QRectF dirtyRect; + uint renderInThread : 1; + uint hasCanvasSize :1; + uint hasTileSize :1; + uint hasCanvasWindow :1; + uint componentCompleted :1; + QQuickCanvasItem::RenderTarget renderTarget; + QHash images; + QUrl baseUrl; +}; + +QQuickCanvasItemPrivate::QQuickCanvasItemPrivate() + : QQuickItemPrivate() + , context(0) + , texture(0) + , canvasSize(1, 1) + , tileSize(1, 1) + , renderInThread(false) + , hasCanvasSize(false) + , hasTileSize(false) + , hasCanvasWindow(false) + , componentCompleted(false) + , renderTarget(QQuickCanvasItem::FramebufferObject) +{ +} + +QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate() +{ + qDeleteAll(images); +} + +/*! + \qmlclass Canvas QQuickCanvasItem + \inqmlmodule QtQuick 2 + \since QtQuick 2.0 + \brief The Canvas item provides HTML5 like canvas element which enables you to + draw within the item area by using Javascript. + \inherits Item + \ingroup qml-basic-visual-elements + + With the Canvas item, users can draw straight and curved lines, simple and + complex shapes, graphs, and referenced graphic images. can also add texts, colors, + shadows, gradients, and patterns, and do low level pixel operations, etc. The Canvas item + also enables you to save or export the canvas as a image file or serialize the image data + to data url string. + + To define a drawing area in the Canvas item, just set the \c width and \c height properties. + For example, the following code creates a Canvas item which has a drawing area with a height of 100 + pixels and width of 200 pixels: + \qml + import QtQuick 2.0 + Canvas { + id:mycanvas + width:100 + height:200 + } + \endqml + + Currently the Canvas item only supports the two-dimensional rendering context. + + \section1 Thread Rendering and Render Target + The Canvas item supports two render targets:Canvas.Image and Canvas.FramebufferObject. + The Canvas.Image render target is a \a QImage object which is actually a block of system + memory. This render target support background thread rendering. So if some complex or long + running painting need to be done, the Canvas.Image with thread rendering mode should be + chosen to avoid blocking the UI. Otherwise the Canvas.FramebufferObject render target should + be chosen as it could be much faster with good OpenGL hardware accelaration than rendering into + system memory, especially when the CPU is already very busy. + + The default render target is Canvas.Image and the default renderInThread property is + false. + + \section1 Tiled Canvas + The Canvas item also supports tiled rendering mode by setting the proper canvasSize, tileSize + and the canvasWindow properties. + + With tiled canvas, a virtually very large canvas can be provided by a relatively small canvas + window. The actual memory consumption only relates to the canvas window size. So the canvas size + can be chosen freely as needed. The painting code then doesn't need to worry about the coordinate + system and complex matrix transformations at all. + + As a side effect, by setting a good tile size, the tiles overlapped with the canvas window could be + cached and don't need to redraw, which can improve the performance significantly in some situations. + + \section1 Pixel Operations + The Canvas item support all HTML5 2d context pixel operations. In order to get better + pixel reading/writing performance, the Canvas.Image render target should be chosen. As + for Canvas.FramebufferObject render target, the pixel data need to be exchanged between + the system memory and the graphic card, which can't be benefit from the hardware acceleration + at all. And the OpenGL rendering may synchronise with the V-Sync signal to avoid the + {en.wikipedia.org/wiki/Screen_tearing}{screen tearing} which makes the pixel operations + even slower with the Canvas.FrambufferObject render target. + + \section1 Tips for Porting Existing HTML5 Canvas applications + + Although the Canvas item is provided as a HTML5 like API, and + the canvas context API is as compatible with HTML5 2d context standard + as possible, the working HTML5 canvas applications are still need to + be modified to run in the Canvas item: + \list + \o Removes and replaces all DOM API calls with QML property bindings or Canvas item methods. + \o Removes and replaces all HTML envent handlers with the \a MouseArea item. + \o Changes the setInterval/setTimeout function calls with the \a Timer item. + \o Puts the actual painting code into the \a QtQuick2::Canvas::onPaint handler and triggers the + painting by calling the Canvas's \c markDirty or \c requestPaint methods. + \o For drawing images, loads them by calling the Canvas's loadImage method and then request to paint + them in the onImageLoaded handler. + \endlist + + \sa QtQuick2::Context2D +*/ + +QQuickCanvasItem::QQuickCanvasItem(QQuickItem *parent) + : QQuickItem(*(new QQuickCanvasItemPrivate), parent) +{ + setFlag(ItemHasContents); +} + +QQuickCanvasItem::~QQuickCanvasItem() +{ + Q_D(QQuickCanvasItem); + delete d->context; +} + +/*! + \qmlproperty size QtQuick2::Canvas::canvasSize + Holds the logical canvas size that the context paints on. + + By default, the canvas size is the same size as the current canvas item size. + By setting the canvas size, tile size and canvas window, the Canvas + item can act as a virtual large canvas with many seperately rendered tile rectangle + areas. Only those tiles within the current canvas window would be painted by + the Canvas render engine. + \sa QtQuick2::Canvas::tileSize QtQuick2::Canvas::canvasWindow +*/ +QSizeF QQuickCanvasItem::canvasSize() const +{ + Q_D(const QQuickCanvasItem); + return d->canvasSize; +} + +void QQuickCanvasItem::setCanvasSize(const QSizeF & size) +{ + Q_D(QQuickCanvasItem); + if (d->canvasSize != size) { + d->hasCanvasSize = true; + d->canvasSize = size; + emit canvasSizeChanged(); + polish(); + update(); + } +} + +/*! + \qmlproperty size QtQuick2::Canvas::tileSize + Holds the canvas rendering tile size. + + When the Canvas item in tiled mode by setting the canvas size, tile size and + the canvas window. The canvas render can improve the rendering performance + by rendering and caching tiles instead of rendering the whole canvas everytime. + + Additionally, the canvas size could be infinitely large without allocating more + memories 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 QtQuick2::Canvas::canvaasSize QtQuick2::Canvas::canvasWindow +*/ +QSize QQuickCanvasItem::tileSize() const +{ + Q_D(const QQuickCanvasItem); + return d->tileSize; +} + +void QQuickCanvasItem::setTileSize(const QSize & size) +{ + Q_D(QQuickCanvasItem); + if (d->tileSize != size) { + d->hasTileSize = true; + d->tileSize = size; + + emit tileSizeChanged(); + polish(); + update(); + } +} + +/*! + \qmlproperty rect QtQuick2::Canvas::canvasWindow + Holds the current canvas visible window. + + By default, the canvas window size is the same as the Canvas item + size with the topleft point as (0, 0). + + If the canvas size is different with the Canvas item size, the Canvas + item can display different visible areas by changing the canvas window's size + and/or position. + \sa QtQuick2::Canvas::canvasSize QtQuick2::Canvas::tileSize +*/ +QRectF QQuickCanvasItem::canvasWindow() const +{ + Q_D(const QQuickCanvasItem); + return d->canvasWindow; +} + +void QQuickCanvasItem::setCanvasWindow(const QRectF& rect) +{ + Q_D(QQuickCanvasItem); + if (d->canvasWindow != rect) { + d->canvasWindow = rect; + + d->hasCanvasWindow = true; + emit canvasWindowChanged(); + polish(); + update(); + } +} + + +QQuickContext2D* QQuickCanvasItem::context() const +{ + Q_D(const QQuickCanvasItem); + return d->context; +} +/*! + \qmlproperty bool QtQuick2::Canvas::renderInThread + Holds the current canvas rendering mode. + + By setting the renderInThread to true, complex and long + running painting can be rendered in a dedicated background + rendering thread to avoid blocking the main GUI. + + Note: Different renderTarget may or may not support the + background rendering thread, if not, the renderInThread + property will be ignored. + + The default value is false. + \sa QtQuick2::Canvas::renderTarget +*/ +bool QQuickCanvasItem::renderInThread() const +{ + Q_D(const QQuickCanvasItem); + return d->renderInThread; +} +/*! + \qmlproperty bool QtQuick2::Canvas::renderTarget + Holds the current canvas render target. + + \list + \o Canvas.Image - render to an in memory image buffer, the render + target supports background rendering. + \o Canvas.FramebufferObject - render to an OpenGL frame buffer, + this render target will ignore the + renderInThread property. The actual + rendering happens in the main QML rendering + process, which may be in a seperate render thread + or in the main GUI thread depends on the platforms. + \endlist + + The default render target is \c Canvas.Image. + \sa QtQuick2::Canvas::renderInThread +*/ +QQuickCanvasItem::RenderTarget QQuickCanvasItem::renderTarget() const +{ + Q_D(const QQuickCanvasItem); + return d->renderTarget; +} + +void QQuickCanvasItem::setRenderTarget(RenderTarget target) +{ + Q_D(QQuickCanvasItem); + if (d->renderTarget != target) { + d->renderTarget = target; + + if (d->componentCompleted) + createTexture(); + emit renderTargetChanged(); + } +} + +void QQuickCanvasItem::_doPainting(const QRectF& region) +{ + Q_D(QQuickCanvasItem); + emit paint(QDeclarativeV8Handle::fromHandle(d->context->v8value()) + , QQuickContext2DTexture::tiledRect(region, d->tileSize)); + if (d->texture) + d->texture->wake(); +} + +/*! + \qmlproperty bool QtQuick2::Canvas::renderInThread + 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 renderInThread value is false. +*/ +void QQuickCanvasItem::setRenderInThread(bool renderInThread) +{ + Q_D(QQuickCanvasItem); + if (d->renderInThread != renderInThread) { + d->renderInThread = renderInThread; + + if (d->componentCompleted) + createTexture(); + + if (d->renderInThread) + connect(this, SIGNAL(painted()), SLOT(update())); + else + disconnect(this, SIGNAL(painted()), this, SLOT(update())); + emit renderInThreadChanged(); + polish(); + update(); + } +} + +void QQuickCanvasItem::geometryChanged(const QRectF &newGeometry, + const QRectF &oldGeometry) +{ + Q_D(QQuickCanvasItem); + QQuickItem::geometryChanged(newGeometry, oldGeometry); + + const qreal w = newGeometry.width(); + const qreal h = newGeometry.height(); + + if (!d->hasCanvasSize) { + d->canvasSize = QSizeF(w, h); + emit canvasSizeChanged(); + } + + if (!d->hasTileSize) { + d->tileSize = d->canvasSize.toSize(); + emit tileSizeChanged(); + } + + if (!d->hasCanvasWindow) { + d->canvasWindow = newGeometry; + emit canvasWindowChanged(); + } + + polish(); + update(); +} + +void QQuickCanvasItem::componentComplete() +{ + Q_D(QQuickCanvasItem); + QQuickItem::componentComplete(); + + if (!d->context) + createContext(); + createTexture(); + + d->baseUrl = qmlEngine(this)->contextForObject(this)->baseUrl(); + requestPaint(); + updatePolish(); //force update the canvas sizes to texture for the first time + update(); + d->componentCompleted = true; +} + +void QQuickCanvasItem::updatePolish() +{ + Q_D(QQuickCanvasItem); + QQuickItem::updatePolish(); + if (d->texture) { + if (!d->renderInThread && d->dirtyRect.isValid()) + _doPainting(d->dirtyRect); + + d->texture->canvasChanged(d->canvasSize.toSize() + , d->tileSize + , d->canvasWindow.toAlignedRect() + , d->dirtyRect.toAlignedRect() + , d->smooth); + d->dirtyRect = QRectF(); + } +} + +QSGNode *QQuickCanvasItem::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *) +{ + Q_D(QQuickCanvasItem); + QQuickContext2DNode *node = static_cast(oldNode); + if (!node) + node = new QQuickContext2DNode(this); + + node->setTexture(d->texture); + node->setSize(d->canvasWindow.size()); + node->update(); + return node; +} + +void QQuickCanvasItem::createTexture() +{ + Q_D(QQuickCanvasItem); + + if (!d->texture + || d->texture->threadRendering() != d->renderInThread + || d->texture->renderTarget() != d->renderTarget) { + if (d->texture) { + d->texture->deleteLater(); + d->texture = 0; + } + + if (d->renderTarget == QQuickCanvasItem::Image) { + d->texture = new QQuickContext2DImageTexture(d->renderInThread); + } else if (d->renderTarget == QQuickCanvasItem::FramebufferObject) { + d->texture = new QQuickContext2DFBOTexture(); + } + + if (d->renderInThread && !d->texture->supportThreadRendering()) { + qWarning("Canvas: render target does not support thread rendering, force to non-thread rendering mode."); + d->renderInThread = false; + emit renderInThreadChanged(); + } + + if (d->renderInThread) + connect(d->texture, SIGNAL(textureChanged()), this, SLOT(update())); + + d->texture->setItem(this); + } +} + +void QQuickCanvasItem::createContext() +{ + Q_D(QQuickCanvasItem); + + delete d->context; + + d->context = new QQuickContext2D(this); + + QV8Engine *e = QDeclarativeEnginePrivate::getV8Engine(qmlEngine(this)); + d->context->setV8Engine(e); +} + +/*! + \qmlmethod object QtQuick2::Canvas::getContext(string contextId) + + Currently, the canvas item only 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 QQuickCanvasItem::getContext(const QString &contextId) +{ + Q_D(QQuickCanvasItem); + + if (contextId.toLower() != QLatin1String("2d")) + return QDeclarativeV8Handle::fromHandle(v8::Undefined()); + + if (!d->context) + createContext(); + return QDeclarativeV8Handle::fromHandle(d->context->v8value()); +} + +/*! + \qmlmethod void QtQuick2::Canvas::markDirty(rect region) + + Mark the given \a region as dirty, so that when this region is visible + the canvas render will redraw it. During the rendering process, the + canvas renderer may emit the canvas' "paint" signal so the actual painting + scripts can be putted into the canvas's "onPaint" signal handler function. + + \sa QtQuick2::Canvas::paint QtQuick2::Canvas::requestPaint + */ +void QQuickCanvasItem::markDirty(const QRectF& region) +{ + Q_D(QQuickCanvasItem); + d->dirtyRect |= region; + if (d->componentCompleted) + polish(); + update(); +} + + +/*! + \qmlmethod bool QtQuick2::Canvas::save(string filename) + + Save the current canvas content into an image file \a filename. + The saved image format is automatically decided by the \a filename's + suffix. + + Note: calling this method will force painting the whole canvas, not the + current canvas visible window. + + \sa canvasWindow canvasSize toDataURL + */ +bool QQuickCanvasItem::save(const QString &filename) const +{ + Q_D(const QQuickCanvasItem); + QUrl url = d->baseUrl.resolved(QUrl::fromLocalFile(filename)); + return toImage().save(url.toLocalFile()); +} + +QImage QQuickCanvasItem::loadedImage(const QUrl& url) +{ + Q_D(QQuickCanvasItem); + QUrl fullPathUrl = d->baseUrl.resolved(url); + if (!d->images.contains(fullPathUrl)) { + loadImage(url); + } + QDeclarativePixmap* pix = d->images.value(fullPathUrl); + if (pix->isLoading() || pix->isError()) { + return QImage(); + } + return pix->pixmap().toImage(); +} + +/*! + \qmlmethod void QtQuick2::Canvas::loadImage(url image) + Loads the given \c image asynchronously, when the image is + ready, an imageLoaded signal will be emitted. + The loaded image can be unloaded by the \a QtQuick2::Canvas::unloadImage method. + + Note: Only loaded images can be painted on the Canvas item. + \sa QtQuick2::Canvas::unloadImage QtQuick2::Canvas::imageLoaded QtQuick2::Canvas::isImageLoaded + \sa QtQuick2::Context2D::createImageData QtQuick2::Context2D::drawImage + */ +void QQuickCanvasItem::loadImage(const QUrl& url) +{ + Q_D(QQuickCanvasItem); + QUrl fullPathUrl = d->baseUrl.resolved(url); + if (!d->images.contains(fullPathUrl)) { + QDeclarativePixmap* pix = new QDeclarativePixmap(); + d->images.insert(fullPathUrl, pix); + + pix->load(qmlEngine(this) + , fullPathUrl + , QDeclarativePixmap::Cache | QDeclarativePixmap::Asynchronous); + pix->connectFinished(this, SIGNAL(imageLoaded())); + } +} +/*! + \qmlmethod void QtQuick2::Canvas::loadImage(url image) + Unloads the \c image. + + If the image is unloaded from the Canvas item, it can't be painted by the canvas context + until it's loaded again. + + \sa QtQuick2::Canvas::loadImage QtQuick2::Canvas::imageLoaded QtQuick2::Canvas::isImageLoaded + \sa QtQuick2::Context2D::createImageData QtQuick2::Context2D::drawImage + */ +void QQuickCanvasItem::unloadImage(const QUrl& url) +{ + Q_D(QQuickCanvasItem); + QUrl removeThis = d->baseUrl.resolved(url); + if (d->images.contains(removeThis)) { + delete d->images.value(removeThis); + d->images.remove(removeThis); + } +} + +/*! + \qmlmethod void QtQuick2::Canvas::isImageError(url image) + Returns true if the image can't be loaded because of error happens. + + \sa QtQuick2::Canvas::loadImage + */ +bool QQuickCanvasItem::isImageError(const QUrl& url) const +{ + Q_D(const QQuickCanvasItem); + QUrl fullPathUrl = d->baseUrl.resolved(url); + return d->images.contains(fullPathUrl) + && d->images.value(fullPathUrl)->isError(); +} + +/*! + \qmlmethod void QtQuick2::Canvas::isImageLoading(url image) + Returns true if the Canvas item still is loading the \c image. + + \sa QtQuick2::Canvas::loadImage + */ +bool QQuickCanvasItem::isImageLoading(const QUrl& url) const +{ + Q_D(const QQuickCanvasItem); + QUrl fullPathUrl = d->baseUrl.resolved(url); + return d->images.contains(fullPathUrl) + && d->images.value(fullPathUrl)->isLoading(); +} +/*! + \qmlmethod void QtQuick2::Canvas::isImageLoaded(url image) + Returns true if the \c image is sucessfully loaded and ready to use. + + \sa QtQuick2::Canvas::loadImage + */ +bool QQuickCanvasItem::isImageLoaded(const QUrl& url) const +{ + Q_D(const QQuickCanvasItem); + QUrl fullPathUrl = d->baseUrl.resolved(url); + return d->images.contains(fullPathUrl) + && d->images.value(fullPathUrl)->isReady(); +} + +QImage QQuickCanvasItem::toImage(const QRectF& region) const +{ + Q_D(const QQuickCanvasItem); + if (d->texture) { + if (region.isEmpty()) + return d->texture->toImage(canvasWindow()); + else + return d->texture->toImage(region); + } + return QImage(); +} + +/*! + \qmlmethod string QtQuick2::Canvas::toDataURL(string mimeType) + + Returns a data: URL for the image in the canvas. + + The default \a mimeType is "image/png". + + \sa QtQuick2::Canvas::save + */ +QString QQuickCanvasItem::toDataURL(const QString& mimeType) const +{ + QImage image = toImage(); + + if (!image.isNull()) { + QByteArray ba; + QBuffer buffer(&ba); + buffer.open(QIODevice::WriteOnly); + QString mime = mimeType.toLower(); + QString type; + if (mime == QLatin1Literal("image/png")) { + type = QLatin1Literal("PNG"); + } else if (mime == QLatin1Literal("image/bmp")) + type = QLatin1Literal("BMP"); + else if (mime == QLatin1Literal("image/jpeg")) + type = QLatin1Literal("JPEG"); + else if (mime == QLatin1Literal("image/x-portable-pixmap")) + type = QLatin1Literal("PPM"); + else if (mime == QLatin1Literal("image/tiff")) + type = QLatin1Literal("TIFF"); + else if (mime == QLatin1Literal("image/xpm")) + type = QLatin1Literal("XPM"); + else + return QLatin1Literal("data:,"); + + image.save(&buffer, type.toAscii()); + buffer.close(); + QString dataUrl = QLatin1Literal("data:%1;base64,%2"); + return dataUrl.arg(mime).arg(QLatin1String(ba.toBase64().constData())); + } + return QLatin1Literal("data:,"); +} + +/*! + \qmlsignal QtQuick2::Canvas::onPaint(QtQuick2::Context2D context, rect region) + + This handler is called before the given \c region needs to be rendered. + + This signal can be triggered by QtQuick2::Canvas::markdirty, QtQuick2::Canvas::requestPaint + or by changing the current canvas window. +*/ + +/*! + \qmlsignal QtQuick2::Canvas::onPainted() + + This handler is called after all context painting commands are executed and + the Canvas is actually rendered. +*/ + +QT_END_NAMESPACE -- cgit v1.2.3