diff options
-rw-r--r-- | src/plugins/scenegraph/d3d12/d3d12.pro | 6 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12context.cpp | 10 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12context_p.h | 2 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12painternode.cpp | 256 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12painternode_p.h | 119 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12rendercontext.cpp | 4 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12shadereffectnode.cpp | 2 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12texture.cpp | 5 | ||||
-rw-r--r-- | src/plugins/scenegraph/d3d12/qsgd3d12texture_p.h | 5 | ||||
-rw-r--r-- | src/quick/items/context2d/qquickcanvasitem.cpp | 26 | ||||
-rw-r--r-- | src/quick/items/qquickpainteditem.cpp | 27 | ||||
-rw-r--r-- | tests/manual/nodetypes/Painter.qml | 84 | ||||
-rw-r--r-- | tests/manual/nodetypes/main.qml | 3 | ||||
-rw-r--r-- | tests/manual/nodetypes/nodetypes.cpp | 104 | ||||
-rw-r--r-- | tests/manual/nodetypes/nodetypes.pro | 2 | ||||
-rw-r--r-- | tests/manual/nodetypes/nodetypes.qrc | 1 |
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> |