aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--src/plugins/scenegraph/d3d12/d3d12.pro6
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12context.cpp10
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12context_p.h2
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp256
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h119
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp4
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp2
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp5
-rw-r--r--src/plugins/scenegraph/d3d12/qsgd3d12texture_p.h5
-rw-r--r--src/quick/items/context2d/qquickcanvasitem.cpp26
-rw-r--r--src/quick/items/qquickpainteditem.cpp27
-rw-r--r--tests/manual/nodetypes/Painter.qml84
-rw-r--r--tests/manual/nodetypes/main.qml3
-rw-r--r--tests/manual/nodetypes/nodetypes.cpp104
-rw-r--r--tests/manual/nodetypes/nodetypes.pro2
-rw-r--r--tests/manual/nodetypes/nodetypes.qrc1
16 files changed, 620 insertions, 36 deletions
diff --git a/src/plugins/scenegraph/d3d12/d3d12.pro b/src/plugins/scenegraph/d3d12/d3d12.pro
index 4ca62bc1a8..6ba18acf22 100644
--- a/src/plugins/scenegraph/d3d12/d3d12.pro
+++ b/src/plugins/scenegraph/d3d12/d3d12.pro
@@ -23,7 +23,8 @@ SOURCES += \
$$PWD/qsgd3d12glyphnode.cpp \
$$PWD/qsgd3d12glyphcache.cpp \
$$PWD/qsgd3d12layer.cpp \
- $$PWD/qsgd3d12shadereffectnode.cpp
+ $$PWD/qsgd3d12shadereffectnode.cpp \
+ $$PWD/qsgd3d12painternode.cpp
NO_PCH_SOURCES += \
$$PWD/qsgd3d12engine.cpp
@@ -44,7 +45,8 @@ HEADERS += \
$$PWD/qsgd3d12glyphnode_p.h \
$$PWD/qsgd3d12glyphcache_p.h \
$$PWD/qsgd3d12layer_p.h \
- $$PWD/qsgd3d12shadereffectnode_p.h
+ $$PWD/qsgd3d12shadereffectnode_p.h \
+ $$PWD/qsgd3d12painternode_p.h
LIBS += -ldxgi -ld3d12 -ld3dcompiler
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp
index 9144794d87..8a0e1a04f7 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12context.cpp
@@ -44,6 +44,7 @@
#include "qsgd3d12glyphnode_p.h"
#include "qsgd3d12layer_p.h"
#include "qsgd3d12shadereffectnode_p.h"
+#include "qsgd3d12painternode_p.h"
QT_BEGIN_NAMESPACE
@@ -64,17 +65,16 @@ QSGImageNode *QSGD3D12Context::createImageNode()
QSGPainterNode *QSGD3D12Context::createPainterNode(QQuickPaintedItem *item)
{
- Q_UNUSED(item);
- Q_UNREACHABLE();
- return nullptr;
+ return new QSGD3D12PainterNode(item);
}
-QSGGlyphNode *QSGD3D12Context::createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode)
+QSGGlyphNode *QSGD3D12Context::createGlyphNode(QSGRenderContext *renderContext, bool preferNativeGlyphNode)
{
Q_UNUSED(preferNativeGlyphNode);
// ### distance field text rendering is not supported atm
- return new QSGD3D12GlyphNode(static_cast<QSGD3D12RenderContext *>(rc));
+ QSGD3D12RenderContext *rc = static_cast<QSGD3D12RenderContext *>(renderContext);
+ return new QSGD3D12GlyphNode(rc);
}
QSGNinePatchNode *QSGD3D12Context::createNinePatchNode()
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h
index 56952519a1..0564c4edac 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12context_p.h
@@ -64,7 +64,7 @@ public:
QSGRectangleNode *createRectangleNode() override;
QSGImageNode *createImageNode() override;
QSGPainterNode *createPainterNode(QQuickPaintedItem *item) override;
- QSGGlyphNode *createGlyphNode(QSGRenderContext *rc, bool preferNativeGlyphNode) override;
+ QSGGlyphNode *createGlyphNode(QSGRenderContext *renderContext, bool preferNativeGlyphNode) override;
QSGNinePatchNode *createNinePatchNode() override;
QSGLayer *createLayer(QSGRenderContext *renderContext) override;
QSGGuiThreadShaderEffectManager *createGuiThreadShaderEffectManager() override;
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp
new file mode 100644
index 0000000000..7837c3a2d3
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp
@@ -0,0 +1,256 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qsgd3d12painternode_p.h"
+#include "qsgd3d12rendercontext_p.h"
+#include "qsgd3d12engine_p.h"
+#include <private/qquickitem_p.h>
+#include <qmath.h>
+
+QT_BEGIN_NAMESPACE
+
+QSGD3D12PainterTexture::QSGD3D12PainterTexture(QSGD3D12Engine *engine)
+ : QSGD3D12Texture(engine)
+{
+}
+
+void QSGD3D12PainterTexture::bind()
+{
+ if (m_image.isNull()) {
+ if (!m_id) {
+ m_id = m_engine->genTexture();
+ m_engine->createTexture(m_id, QSize(16, 16), QImage::Format_RGB32, 0);
+ }
+ } else if (m_image.size() != lastSize) {
+ lastSize = m_image.size();
+ if (m_id)
+ m_engine->releaseTexture(m_id);
+ m_id = m_engine->genTexture();
+ m_engine->createTexture(m_id, m_image.size(), m_image.format(), QSGD3D12Engine::TextureWithAlpha);
+ m_engine->queueTextureUpload(m_id, m_image);
+ } else if (!dirty.isEmpty()) {
+ const int bpl = m_image.bytesPerLine();
+ const uchar *p = m_image.constBits() + dirty.y() * bpl + dirty.x() * 4;
+ QImage subImg(p, dirty.width(), dirty.height(), bpl, QImage::Format_ARGB32_Premultiplied);
+ m_engine->queueTextureUpload(m_id, subImg, dirty.topLeft());
+ }
+
+ dirty = QRect();
+
+ m_engine->useTexture(m_id);
+}
+
+QSGD3D12PainterNode::QSGD3D12PainterNode(QQuickPaintedItem *item)
+ : m_item(item),
+ m_engine(static_cast<QSGD3D12RenderContext *>(QQuickItemPrivate::get(item)->sceneGraphRenderContext())->engine()),
+ m_texture(new QSGD3D12PainterTexture(m_engine)),
+ m_geometry(QSGGeometry::defaultAttributes_TexturedPoint2D(), 4),
+ m_dirtyGeometry(false),
+ m_dirtyContents(false)
+{
+ m_material.setFlag(QSGMaterial::Blending);
+ m_material.setTexture(m_texture);
+ setMaterial(&m_material);
+ setGeometry(&m_geometry);
+}
+
+QSGD3D12PainterNode::~QSGD3D12PainterNode()
+{
+ delete m_texture;
+}
+
+void QSGD3D12PainterNode::setPreferredRenderTarget(QQuickPaintedItem::RenderTarget)
+{
+ // always QImage-based
+}
+
+void QSGD3D12PainterNode::setSize(const QSize &size)
+{
+ if (m_size == size)
+ return;
+
+ m_size = size;
+ m_dirtyGeometry = true;
+}
+
+void QSGD3D12PainterNode::setDirty(const QRect &dirtyRect)
+{
+ m_dirtyRect = dirtyRect;
+ m_dirtyContents = true;
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12PainterNode::setOpaquePainting(bool)
+{
+ // ignored
+}
+
+void QSGD3D12PainterNode::setLinearFiltering(bool linearFiltering)
+{
+ m_material.setFiltering(linearFiltering ? QSGTexture::Linear : QSGTexture::Nearest);
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12PainterNode::setMipmapping(bool)
+{
+ // ### not yet
+}
+
+void QSGD3D12PainterNode::setSmoothPainting(bool s)
+{
+ if (m_smoothPainting == s)
+ return;
+
+ m_smoothPainting = s;
+ m_dirtyContents = true;
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12PainterNode::setFillColor(const QColor &c)
+{
+ if (m_fillColor == c)
+ return;
+
+ m_fillColor = c;
+ m_dirtyContents = true;
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12PainterNode::setContentsScale(qreal s)
+{
+ if (m_contentsScale == s)
+ return;
+
+ m_contentsScale = s;
+ m_dirtyContents = true;
+ markDirty(DirtyMaterial);
+}
+
+void QSGD3D12PainterNode::setFastFBOResizing(bool)
+{
+ // nope
+}
+
+void QSGD3D12PainterNode::setTextureSize(const QSize &size)
+{
+ if (m_textureSize == size)
+ return;
+
+ m_textureSize = size;
+ m_dirtyGeometry = true;
+}
+
+QImage QSGD3D12PainterNode::toImage() const
+{
+ return *m_texture->image();
+}
+
+void QSGD3D12PainterNode::update()
+{
+ if (m_dirtyGeometry) {
+ m_dirtyGeometry = false;
+ QRectF src(0, 0, 1, 1);
+ QRectF dst(QPointF(0, 0), m_size);
+ QSGGeometry::updateTexturedRectGeometry(&m_geometry, dst, src);
+ markDirty(DirtyGeometry);
+ }
+
+ QImage *img = m_texture->image();
+ if (img->size() != m_textureSize) {
+ *img = QImage(m_textureSize, QImage::Format_ARGB32_Premultiplied);
+ img->fill(Qt::transparent);
+ m_dirtyContents = true;
+ }
+
+ if (m_dirtyContents) {
+ m_dirtyContents = false;
+ if (!img->isNull()) {
+ QRect dirtyRect = m_dirtyRect.isNull() ? QRect(QPoint(0, 0), m_size) : m_dirtyRect;
+ QPainter painter;
+ painter.begin(img);
+ if (m_smoothPainting)
+ painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing | QPainter::SmoothPixmapTransform);
+
+ QRect clipRect;
+ QRect dirtyTextureRect;
+
+ if (m_contentsScale == 1) {
+ float scaleX = m_textureSize.width() / (float) m_size.width();
+ float scaleY = m_textureSize.height() / (float) m_size.height();
+ painter.scale(scaleX, scaleY);
+ clipRect = dirtyRect;
+ dirtyTextureRect = QRectF(dirtyRect.x() * scaleX,
+ dirtyRect.y() * scaleY,
+ dirtyRect.width() * scaleX,
+ dirtyRect.height() * scaleY).toAlignedRect();
+ } else {
+ painter.scale(m_contentsScale, m_contentsScale);
+ QRect sclip(qFloor(dirtyRect.x() / m_contentsScale),
+ qFloor(dirtyRect.y() / m_contentsScale),
+ qCeil(dirtyRect.width() / m_contentsScale + dirtyRect.x() / m_contentsScale
+ - qFloor(dirtyRect.x() / m_contentsScale)),
+ qCeil(dirtyRect.height() / m_contentsScale + dirtyRect.y() / m_contentsScale
+ - qFloor(dirtyRect.y() / m_contentsScale)));
+ clipRect = sclip;
+ dirtyTextureRect = dirtyRect;
+ }
+
+ // only clip if we were originally updating only a subrect
+ if (!m_dirtyRect.isNull())
+ painter.setClipRect(clipRect);
+
+ painter.setCompositionMode(QPainter::CompositionMode_Source);
+ painter.fillRect(clipRect, m_fillColor);
+ painter.setCompositionMode(QPainter::CompositionMode_SourceOver);
+
+ m_item->paint(&painter);
+ painter.end();
+
+ m_texture->dirty = dirtyTextureRect;
+ }
+ m_dirtyRect = QRect();
+ }
+}
+
+QSGTexture *QSGD3D12PainterNode::texture() const
+{
+ return m_texture;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h
new file mode 100644
index 0000000000..67246cc3cd
--- /dev/null
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h
@@ -0,0 +1,119 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQuick module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QSGD3D12PAINTERNODE_P_H
+#define QSGD3D12PAINTERNODE_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 <private/qsgadaptationlayer_p.h>
+#include "qsgd3d12texture_p.h"
+#include "qsgd3d12builtinmaterials_p.h"
+
+QT_BEGIN_NAMESPACE
+
+class QSGD3D12Engine;
+
+class QSGD3D12PainterTexture : public QSGD3D12Texture
+{
+public:
+ QSGD3D12PainterTexture(QSGD3D12Engine *engine);
+
+ void bind() override;
+
+ QImage *image() { return &m_image; }
+
+ QRect dirty;
+
+private:
+ QSize lastSize;
+};
+
+class QSGD3D12PainterNode : public QSGPainterNode
+{
+public:
+ QSGD3D12PainterNode(QQuickPaintedItem *item);
+ ~QSGD3D12PainterNode();
+
+ void setPreferredRenderTarget(QQuickPaintedItem::RenderTarget target) override;
+ void setSize(const QSize &size) override;
+ void setDirty(const QRect &dirtyRect = QRect()) override;
+ void setOpaquePainting(bool opaque) override;
+ void setLinearFiltering(bool linearFiltering) override;
+ void setMipmapping(bool mipmapping) override;
+ void setSmoothPainting(bool s) override;
+ void setFillColor(const QColor &c) override;
+ void setContentsScale(qreal s) override;
+ void setFastFBOResizing(bool dynamic) override;
+ void setTextureSize(const QSize &size) override;
+
+ QImage toImage() const override;
+ void update() override;
+ QSGTexture *texture() const override;
+
+private:
+ QQuickPaintedItem *m_item;
+ QSGD3D12Engine *m_engine;
+ QSGD3D12PainterTexture *m_texture;
+ QSize m_size;
+ QSize m_textureSize;
+ float m_contentsScale = 1;
+ bool m_smoothPainting = false;
+ QColor m_fillColor = Qt::transparent;
+ QRect m_dirtyRect;
+
+ QSGGeometry m_geometry;
+ QSGD3D12TextureMaterial m_material;
+
+ uint m_dirtyGeometry : 1;
+ uint m_dirtyContents : 1;
+};
+
+QT_END_NAMESPACE
+
+#endif // QSGD3D12PAINTERNODE_P_H
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp
index ab590b95c5..8a4fd678e7 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp
@@ -92,11 +92,11 @@ QSGTexture *QSGD3D12RenderContext::createTexture(const QImage &image, uint flags
{
Q_ASSERT(m_engine);
QSGD3D12Texture *t = new QSGD3D12Texture(m_engine);
- t->setImage(image, flags);
+ t->create(image, flags);
return t;
}
- QSGRenderer *QSGD3D12RenderContext::createRenderer()
+QSGRenderer *QSGD3D12RenderContext::createRenderer()
{
return new QSGD3D12Renderer(this);
}
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp
index dbdc876930..b4288c2ef5 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp
@@ -495,7 +495,7 @@ QSGD3D12Material::UpdateResults QSGD3D12ShaderEffectMaterial::updatePipeline(con
dummy = new QSGD3D12Texture(node->renderContext()->engine());
QImage img(128, 128, QImage::Format_ARGB32_Premultiplied);
img.fill(0);
- dummy->setImage(img, QSGRenderContext::CreateTexture_Alpha);
+ dummy->create(img, QSGRenderContext::CreateTexture_Alpha);
}
tv.filter = QSGD3D12TextureView::FilterNearest;
tv.addressModeHoriz = QSGD3D12TextureView::AddressWrap;
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp b/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp
index 75dccba744..ce0c0e53dd 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp
@@ -43,7 +43,7 @@
QT_BEGIN_NAMESPACE
-void QSGD3D12Texture::setImage(const QImage &image, uint flags)
+void QSGD3D12Texture::create(const QImage &image, uint flags)
{
// ### atlas?
@@ -51,7 +51,6 @@ void QSGD3D12Texture::setImage(const QImage &image, uint flags)
m_alphaWanted = alphaRequest && image.hasAlphaChannel();
m_image = image;
- m_size = image.size();
m_id = m_engine->genTexture();
Q_ASSERT(m_id);
@@ -75,7 +74,7 @@ int QSGD3D12Texture::textureId() const
QSize QSGD3D12Texture::textureSize() const
{
- return m_size;
+ return m_image.size();
}
bool QSGD3D12Texture::hasAlphaChannel() const
diff --git a/src/plugins/scenegraph/d3d12/qsgd3d12texture_p.h b/src/plugins/scenegraph/d3d12/qsgd3d12texture_p.h
index b9d53dd1e6..06013d4bea 100644
--- a/src/plugins/scenegraph/d3d12/qsgd3d12texture_p.h
+++ b/src/plugins/scenegraph/d3d12/qsgd3d12texture_p.h
@@ -64,7 +64,7 @@ public:
QSGD3D12Texture(QSGD3D12Engine *engine) : m_engine(engine) { }
~QSGD3D12Texture();
- void setImage(const QImage &image, uint flags);
+ void create(const QImage &image, uint flags);
int textureId() const override;
QSize textureSize() const override;
@@ -75,14 +75,13 @@ public:
SIZE_T srv() const;
-private:
+protected:
QSGD3D12Engine *m_engine;
QImage m_image;
bool m_createPending = false;
bool m_createdWithMipMaps = false;
uint m_id = 0;
bool m_alphaWanted = false;
- QSize m_size;
};
QT_END_NAMESPACE
diff --git a/src/quick/items/context2d/qquickcanvasitem.cpp b/src/quick/items/context2d/qquickcanvasitem.cpp
index 6603d8158d..f654261cb2 100644
--- a/src/quick/items/context2d/qquickcanvasitem.cpp
+++ b/src/quick/items/context2d/qquickcanvasitem.cpp
@@ -255,17 +255,18 @@ QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate()
The Canvas item supports two render targets: \c Canvas.Image and
\c Canvas.FramebufferObject.
- The \c Canvas.Image render target is a \a QImage object. This render
- target supports background thread rendering, allowing complex or long
- running painting to be executed without blocking the UI.
+ The \c Canvas.Image render target is a \a QImage object. This render target
+ supports background thread rendering, allowing complex or long running
+ painting to be executed without blocking the UI. This is the only render
+ target that is supported by all Qt Quick backends.
The Canvas.FramebufferObject render target utilizes OpenGL hardware
acceleration rather than rendering into system memory, which in many cases
- results in faster rendering. Canvas.FramebufferObject relies on the
- OpenGL extensions \c GL_EXT_framebuffer_multisample and
- \c GL_EXT_framebuffer_blit for antialiasing. It will also use more
- graphics memory when rendering strategy is anything other than
- Canvas.Cooperative.
+ results in faster rendering. Canvas.FramebufferObject relies on the OpenGL
+ extensions \c GL_EXT_framebuffer_multisample and \c GL_EXT_framebuffer_blit
+ for antialiasing. It will also use more graphics memory when rendering
+ strategy is anything other than Canvas.Cooperative. Framebuffer objects may
+ not be available with Qt Quick backends other than OpenGL.
The default render target is Canvas.Image and the default renderStrategy is
Canvas.Immediate.
@@ -300,7 +301,14 @@ QQuickCanvasItemPrivate::~QQuickCanvasItemPrivate()
and can be used directly in \l {ShaderEffect}{ShaderEffects} and other
classes that consume texture providers.
- \sa Context2D
+ \note In general large canvases, frequent updates, and animation should be
+ avoided with the Canvas.Image render target. This is because with
+ accelerated graphics APIs each update will lead to a texture upload. Also,
+ if possible, prefer QQuickPaintedItem and implement drawing in C++ via
+ QPainter instead of the more expensive and likely less performing
+ JavaScript and Context2D approach.
+
+ \sa Context2D QQuickPaintedItem
*/
QQuickCanvasItem::QQuickCanvasItem(QQuickItem *parent)
diff --git a/src/quick/items/qquickpainteditem.cpp b/src/quick/items/qquickpainteditem.cpp
index e4a20b9787..5813b4b115 100644
--- a/src/quick/items/qquickpainteditem.cpp
+++ b/src/quick/items/qquickpainteditem.cpp
@@ -63,13 +63,14 @@ public:
\inmodule QtQuick
- The QQuickPaintedItem makes it possible to use the QPainter API with the QML Scene Graph.
- It sets up a textured rectangle in the Scene Graph and uses a QPainter to paint
- onto the texture. The render target can be either a QImage or a QOpenGLFramebufferObject.
- When the render target is a QImage, QPainter first renders into the image then
- the content is uploaded to the texture.
- When a QOpenGLFramebufferObject is used, QPainter paints directly onto the texture.
- Call update() to trigger a repaint.
+ The QQuickPaintedItem makes it possible to use the QPainter API with the
+ QML Scene Graph. It sets up a textured rectangle in the Scene Graph and
+ uses a QPainter to paint onto the texture. The render target can be either
+ a QImage or, when OpenGL is in use, a QOpenGLFramebufferObject. When the
+ render target is a QImage, QPainter first renders into the image then the
+ content is uploaded to the texture. When a QOpenGLFramebufferObject is
+ used, QPainter paints directly onto the texture. Call update() to trigger a
+ repaint.
To enable QPainter to do anti-aliased rendering, use setAntialiasing().
@@ -78,6 +79,10 @@ public:
public function: paint(), which implements the actual painting. The
painting will be inside the rectangle spanning from 0,0 to
width(),height().
+
+ \note It important to understand the performance implications such items
+ can incur. See QQuickPaintedItem::RenderTarget and
+ QQuickPaintedItem::renderTarget.
*/
/*!
@@ -172,8 +177,6 @@ QQuickPaintedItem::~QQuickPaintedItem()
is processed by the QML Scene Graph when the next frame is rendered. The item will only be
redrawn if it is visible.
- Note that calling this function will trigger a repaint of the whole scene.
-
\sa paint()
*/
void QQuickPaintedItem::update(const QRect &rect)
@@ -499,6 +502,12 @@ void QQuickPaintedItem::setFillColor(const QColor &c)
the QQuickPaintedItem::FramebufferObject render target if the item gets resized often.
By default, the render target is QQuickPaintedItem::Image.
+
+ \note Some Qt Quick backends may not support all render target options. For
+ example, it is likely that non-OpenGL backends will lack support for
+ QQuickPaintedItem::FramebufferObject and
+ QQuickPaintedItem::InvertedYFramebufferObject. Requesting these will then
+ be ignored.
*/
QQuickPaintedItem::RenderTarget QQuickPaintedItem::renderTarget() const
{
diff --git a/tests/manual/nodetypes/Painter.qml b/tests/manual/nodetypes/Painter.qml
new file mode 100644
index 0000000000..a5973379f4
--- /dev/null
+++ b/tests/manual/nodetypes/Painter.qml
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 The Qt Company Ltd.
+** Contact: http://www.qt.io/licensing/
+**
+** This file is part of the demonstration applications of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:BSD$
+** You may use this file under the terms of the BSD license as follows:
+**
+** "Redistribution and use in source and binary forms, with or without
+** modification, are permitted provided that the following conditions are
+** met:
+** * Redistributions of source code must retain the above copyright
+** notice, this list of conditions and the following disclaimer.
+** * Redistributions in binary form must reproduce the above copyright
+** notice, this list of conditions and the following disclaimer in
+** the documentation and/or other materials provided with the
+** distribution.
+** * Neither the name of The Qt Company Ltd nor the names of its
+** contributors may be used to endorse or promote products derived
+** from this software without specific prior written permission.
+**
+**
+** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
+** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+import QtQuick 2.0
+import Stuff 1.0
+
+Item {
+ ListModel {
+ id: balloonModel
+ ListElement {
+ balloonWidth: 200
+ }
+ ListElement {
+ balloonWidth: 120
+ }
+ ListElement {
+ balloonWidth: 120
+ }
+ ListElement {
+ balloonWidth: 120
+ }
+ ListElement {
+ balloonWidth: 120
+ }
+ }
+
+ ListView {
+ anchors.fill: parent
+ anchors.margins: 10
+ id: balloonView
+ model: balloonModel
+ spacing: 5
+ delegate: TextBalloon {
+ anchors.right: index % 2 == 0 ? undefined : parent.right
+ height: 60
+ rightAligned: index % 2 == 0 ? false : true
+ width: balloonWidth
+ innerAnim: model.index === 1
+ NumberAnimation on width {
+ from: 200
+ to: 300
+ duration: 5000
+ running: model.index === 0
+ }
+ }
+ }
+}
diff --git a/tests/manual/nodetypes/main.qml b/tests/manual/nodetypes/main.qml
index 10f5840262..87d42da3bb 100644
--- a/tests/manual/nodetypes/main.qml
+++ b/tests/manual/nodetypes/main.qml
@@ -71,5 +71,8 @@ Item {
if (event.key === Qt.Key_E)
loader.source = "qrc:/Effects.qml";
+
+ if (event.key === Qt.Key_P)
+ loader.source = "qrc:/Painter.qml";
}
}
diff --git a/tests/manual/nodetypes/nodetypes.cpp b/tests/manual/nodetypes/nodetypes.cpp
index 3ebae43c00..ac54e069ed 100644
--- a/tests/manual/nodetypes/nodetypes.cpp
+++ b/tests/manual/nodetypes/nodetypes.cpp
@@ -43,6 +43,108 @@
#include <QQuickView>
#include <QQmlEngine>
#include <QQmlContext>
+#include <QQuickPaintedItem>
+#include <QPainter>
+#include <QTimer>
+
+class TextBalloon : public QQuickPaintedItem
+{
+ Q_OBJECT
+ Q_PROPERTY(bool rightAligned READ isRightAligned WRITE setRightAligned NOTIFY rightAlignedChanged)
+ Q_PROPERTY(bool innerAnim READ innerAnimEnabled WRITE setInnerAnimEnabled NOTIFY innerAnimChanged)
+
+public:
+ TextBalloon(QQuickItem *parent = nullptr) : QQuickPaintedItem(parent) {
+ connect(&m_timer, &QTimer::timeout, this, &TextBalloon::onAnim);
+ m_timer.setInterval(500);
+ }
+ void paint(QPainter *painter);
+
+ bool isRightAligned() { return m_rightAligned; }
+ void setRightAligned(bool rightAligned);
+
+ bool innerAnimEnabled() const { return m_innerAnim; }
+ void setInnerAnimEnabled(bool b);
+
+signals:
+ void rightAlignedChanged();
+ void innerAnimChanged();
+
+private slots:
+ void onAnim();
+
+private:
+ bool m_rightAligned = false;
+ bool m_innerAnim = false;
+ QTimer m_timer;
+ QRect m_animRect = QRect(10, 10, 50, 20);
+ int m_anim = 0;
+};
+
+void TextBalloon::paint(QPainter *painter)
+{
+ QBrush brush(QColor("#007430"));
+
+ painter->setBrush(brush);
+ painter->setPen(Qt::NoPen);
+ painter->setRenderHint(QPainter::Antialiasing);
+
+ painter->drawRoundedRect(0, 0, boundingRect().width(), boundingRect().height() - 10, 10, 10);
+
+ if (m_rightAligned) {
+ const QPointF points[3] = {
+ QPointF(boundingRect().width() - 10.0, boundingRect().height() - 10.0),
+ QPointF(boundingRect().width() - 20.0, boundingRect().height()),
+ QPointF(boundingRect().width() - 30.0, boundingRect().height() - 10.0),
+ };
+ painter->drawConvexPolygon(points, 3);
+ } else {
+ const QPointF points[3] = {
+ QPointF(10.0, boundingRect().height() - 10.0),
+ QPointF(20.0, boundingRect().height()),
+ QPointF(30.0, boundingRect().height() - 10.0),
+ };
+ painter->drawConvexPolygon(points, 3);
+ }
+
+ if (m_innerAnim) {
+ painter->fillRect(m_animRect, Qt::lightGray);
+ const int x = m_animRect.x() + m_anim;
+ const int y = m_animRect.y() + m_animRect.height() / 2;
+ painter->setPen(QPen(QBrush(Qt::SolidLine), 4));
+ painter->drawLine(x + 4, y, x + 10, y);
+ m_anim += 10;
+ if (m_anim > m_animRect.width())
+ m_anim = 0;
+ }
+}
+
+void TextBalloon::setRightAligned(bool rightAligned)
+{
+ if (m_rightAligned == rightAligned)
+ return;
+
+ m_rightAligned = rightAligned;
+ emit rightAlignedChanged();
+}
+
+void TextBalloon::setInnerAnimEnabled(bool b)
+{
+ if (m_innerAnim == b)
+ return;
+
+ m_innerAnim = b;
+ if (!b)
+ m_timer.stop();
+ else
+ m_timer.start();
+ emit innerAnimChanged();
+}
+
+void TextBalloon::onAnim()
+{
+ update(m_animRect);
+}
class Helper : public QObject
{
@@ -68,6 +170,7 @@ int main(int argc, char **argv)
qDebug(" [A] - Render thread Animator");
qDebug(" [L] - Layers");
qDebug(" [E] - Effects");
+ qDebug(" [P] - QQuickPaintedItem");
qDebug("\nPress S to stop the currently running test\n");
Helper helper;
@@ -79,6 +182,7 @@ int main(int argc, char **argv)
view.setFormat(fmt);
}
view.engine()->rootContext()->setContextProperty(QLatin1String("helper"), &helper);
+ qmlRegisterType<TextBalloon>("Stuff", 1, 0, "TextBalloon");
view.setResizeMode(QQuickView::SizeRootObjectToView);
view.resize(1024, 768);
view.setSource(QUrl("qrc:/main.qml"));
diff --git a/tests/manual/nodetypes/nodetypes.pro b/tests/manual/nodetypes/nodetypes.pro
index 43b79323a8..afb5a46a84 100644
--- a/tests/manual/nodetypes/nodetypes.pro
+++ b/tests/manual/nodetypes/nodetypes.pro
@@ -5,5 +5,5 @@ SOURCES += nodetypes.cpp
RESOURCES += nodetypes.qrc
OTHER_FILES += main.qml Rects.qml LotsOfRects.qml \
- Images.qml Text.qml Animators.qml Layers.qml Effects.qml \
+ Images.qml Text.qml Animators.qml Layers.qml Effects.qml Painter.qml \
wobble.hlsl shadow1.hlsl shadow2.hlsl
diff --git a/tests/manual/nodetypes/nodetypes.qrc b/tests/manual/nodetypes/nodetypes.qrc
index 64bd503319..56fa2b6ee4 100644
--- a/tests/manual/nodetypes/nodetypes.qrc
+++ b/tests/manual/nodetypes/nodetypes.qrc
@@ -8,6 +8,7 @@
<file>Animators.qml</file>
<file>Layers.qml</file>
<file>Effects.qml</file>
+ <file>Painter.qml</file>
<file>qt.png</file>
<file>face-smile.png</file>
<file>shadow.png</file>