Revision control
++ // Copyright (C) 2016 The Qt Company Ltd.
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #include "qquickshadereffectsource_p.h"
++
++ #include "qquickitem_p.h"
++ #include "qquickwindow_p.h"
++ #include <private/qsgadaptationlayer_p.h>
++ #include <QtQuick/private/qsgrenderer_p.h>
++ #include <qsgsimplerectnode.h>
++
++ #include "qmath.h"
++ #include <QtQuick/private/qsgtexture_p.h>
++ #include <QtCore/QRunnable>
++
++ QT_BEGIN_NAMESPACE
++
++ class QQuickShaderEffectSourceTextureProvider : public QSGTextureProvider
++ {
++ Q_OBJECT
++ public:
++ QQuickShaderEffectSourceTextureProvider()
++ : sourceTexture(nullptr)
++ , mipmapFiltering(QSGTexture::None)
++ , filtering(QSGTexture::Nearest)
++ , horizontalWrap(QSGTexture::ClampToEdge)
++ , verticalWrap(QSGTexture::ClampToEdge)
++ {
++ }
++
++ QSGTexture *texture() const override {
++ sourceTexture->setMipmapFiltering(mipmapFiltering);
++ sourceTexture->setFiltering(filtering);
++ sourceTexture->setHorizontalWrapMode(horizontalWrap);
++ sourceTexture->setVerticalWrapMode(verticalWrap);
++ return sourceTexture;
++ }
++
++ QSGLayer *sourceTexture;
++
++ QSGTexture::Filtering mipmapFiltering;
++ QSGTexture::Filtering filtering;
++ QSGTexture::WrapMode horizontalWrap;
++ QSGTexture::WrapMode verticalWrap;
++ };
++
++ class QQuickShaderEffectSourceCleanup : public QRunnable
++ {
++ public:
++ QQuickShaderEffectSourceCleanup(QSGLayer *t, QQuickShaderEffectSourceTextureProvider *p)
++ : texture(t)
++ , provider(p)
++ {}
++ void run() override {
++ delete texture;
++ delete provider;
++ }
++ QSGLayer *texture;
++ QQuickShaderEffectSourceTextureProvider *provider;
++ };
++
++ /*!
++ \qmltype ShaderEffectSource
++ \nativetype QQuickShaderEffectSource
++ \inqmlmodule QtQuick
++ \since 5.0
++ \inherits Item
++ \ingroup qtquick-effects
++ \brief Renders a \l {Qt Quick} item into a texture and displays it.
++
++ The ShaderEffectSource type renders \l sourceItem into a texture and
++ displays it in the scene. \l sourceItem is drawn into the texture as though
++ it was a fully opaque root item. Thus \l sourceItem itself can be
++ invisible, but still appear in the texture.
++
++ You can use the ShaderEffectSource as:
++ \list
++ \li a texture source in a \l ShaderEffect.
++ This allows you to apply custom shader effects to any \l {Qt Quick} item.
++ \li a cache for a complex item.
++ The complex item can be rendered once into the texture, which can
++ then be animated freely without the need to render the complex item
++ again every frame.
++ \li an opacity layer.
++ ShaderEffectSource allows you to apply an opacity to items as a group
++ rather than each item individually.
++ \endlist
++
++ \table
++ \row
++ \li \image declarative-shadereffectsource.png {Overlapping colored
++ rectangles rendered as shader effect source}
++ \li \qml
++ import QtQuick 2.0
++
++ Rectangle {
++ width: 200
++ height: 100
++ gradient: Gradient {
++ GradientStop { position: 0; color: "white" }
++ GradientStop { position: 1; color: "black" }
++ }
++ Row {
++ opacity: 0.5
++ Item {
++ id: foo
++ width: 100; height: 100
++ Rectangle { x: 5; y: 5; width: 60; height: 60; color: "red" }
++ Rectangle { x: 20; y: 20; width: 60; height: 60; color: "orange" }
++ Rectangle { x: 35; y: 35; width: 60; height: 60; color: "yellow" }
++ }
++ ShaderEffectSource {
++ width: 100; height: 100
++ sourceItem: foo
++ }
++ }
++ }
++ \endqml
++ \endtable
++
++ The ShaderEffectSource type does not redirect any mouse or keyboard
++ input to \l sourceItem. If you hide the \l sourceItem by setting
++ \l{Item::visible}{visible} to false or \l{Item::opacity}{opacity} to zero,
++ it will no longer react to input. In cases where the ShaderEffectSource is
++ meant to replace the \l sourceItem, you typically want to hide the
++ \l sourceItem while still handling input. For this, you can use
++ the \l hideSource property.
++
++ \include notes.qdocinc shadereffectsource and multieffect
++
++ \note The ShaderEffectSource relies on FBO multisampling support
++ to antialias edges. If the underlying hardware does not support this,
++ which is the case for most embedded graphics chips, edges rendered
++ inside a ShaderEffectSource will not be antialiased. One way to remedy
++ this is to double the size of the effect source and render it with
++ \c {smooth: true} (this is the default value of smooth).
++ This will be equivalent to 4x multisampling, at the cost of lower performance
++ and higher memory use.
++
++ \warning In most cases, using a ShaderEffectSource will decrease
++ performance, and in all cases, it will increase video memory usage.
++ Rendering through a ShaderEffectSource might also lead to lower quality
++ since some OpenGL implementations support multisampled backbuffer,
++ but not multisampled framebuffer objects.
++ */
++
++ QQuickShaderEffectSource::QQuickShaderEffectSource(QQuickItem *parent)
++ : QQuickItem(parent)
++ , m_provider(nullptr)
++ , m_texture(nullptr)
++ , m_wrapMode(ClampToEdge)
++ , m_sourceItem(nullptr)
++ , m_textureSize(0, 0)
++ , m_format(RGBA8)
++ , m_samples(0)
++ , m_live(true)
++ , m_hideSource(false)
++ , m_mipmap(false)
++ , m_recursive(false)
++ , m_grab(true)
++ , m_textureMirroring(MirrorVertically)
++ {
++ setFlag(ItemHasContents);
++ }
++
++ QQuickShaderEffectSource::~QQuickShaderEffectSource()
++ {
++ if (window()) {
++ window()->scheduleRenderJob(new QQuickShaderEffectSourceCleanup(m_texture, m_provider),
++ QQuickWindow::AfterSynchronizingStage);
++ } else {
++ // If we don't have a window, these should already have been
++ // released in invalidateSG or in releaseResrouces()
++ Q_ASSERT(!m_texture);
++ Q_ASSERT(!m_provider);
++ }
++
++ if (m_sourceItem) {
++ QQuickItemPrivate *sd = QQuickItemPrivate::get(m_sourceItem);
++ sd->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
++ sd->derefFromEffectItem(m_hideSource);
++ if (window())
++ sd->derefWindow();
++ }
++ }
++
++ void QQuickShaderEffectSource::ensureTexture()
++ {
++ if (m_texture)
++ return;
++
++ Q_ASSERT_X(QQuickItemPrivate::get(this)->window
++ && QQuickItemPrivate::get(this)->sceneGraphRenderContext()
++ && QThread::currentThread() == QQuickItemPrivate::get(this)->sceneGraphRenderContext()->thread(),
++ "QQuickShaderEffectSource::ensureTexture",
++ "Cannot be used outside the rendering thread");
++
++ QSGRenderContext *rc = QQuickItemPrivate::get(this)->sceneGraphRenderContext();
++ m_texture = rc->sceneGraphContext()->createLayer(rc);
++ connect(QQuickItemPrivate::get(this)->window, SIGNAL(sceneGraphInvalidated()), m_texture, SLOT(invalidated()), Qt::DirectConnection);
++ connect(m_texture, SIGNAL(updateRequested()), this, SLOT(update()));
++ connect(m_texture, SIGNAL(scheduledUpdateCompleted()), this, SIGNAL(scheduledUpdateCompleted()));
++ }
++
++ static void get_wrap_mode(QQuickShaderEffectSource::WrapMode mode, QSGTexture::WrapMode *hWrap, QSGTexture::WrapMode *vWrap);
++
++ QSGTextureProvider *QQuickShaderEffectSource::textureProvider() const
++ {
++ const QQuickItemPrivate *d = QQuickItemPrivate::get(this);
++ if (!d->window || !d->sceneGraphRenderContext() || QThread::currentThread() != d->sceneGraphRenderContext()->thread()) {
++ qWarning("QQuickShaderEffectSource::textureProvider: can only be queried on the rendering thread of an exposed window");
++ return nullptr;
++ }
++
++ if (!m_provider) {
++ const_cast<QQuickShaderEffectSource *>(this)->m_provider = new QQuickShaderEffectSourceTextureProvider();
++ const_cast<QQuickShaderEffectSource *>(this)->ensureTexture();
++ connect(m_texture, SIGNAL(updateRequested()), m_provider, SIGNAL(textureChanged()));
++
++ get_wrap_mode(m_wrapMode, &m_provider->horizontalWrap, &m_provider->verticalWrap);
++ m_provider->mipmapFiltering = mipmap() ? QSGTexture::Linear : QSGTexture::None;
++ m_provider->filtering = smooth() ? QSGTexture::Linear : QSGTexture::Nearest;
++ m_provider->sourceTexture = m_texture;
++ }
++ return m_provider;
++ }
++
++ /*!
++ \qmlproperty enumeration QtQuick::ShaderEffectSource::wrapMode
++
++ This property defines the OpenGL wrap modes associated with the texture.
++ Modifying this property makes most sense when the item is used as a
++ source texture of a \l ShaderEffect.
++
++ The default value is \c{ShaderEffectSource.ClampToEdge}.
++
++ \value ShaderEffectSource.ClampToEdge GL_CLAMP_TO_EDGE both horizontally and vertically
++ \value ShaderEffectSource.RepeatHorizontally GL_REPEAT horizontally, GL_CLAMP_TO_EDGE vertically
++ \value ShaderEffectSource.RepeatVertically GL_CLAMP_TO_EDGE horizontally, GL_REPEAT vertically
++ \value ShaderEffectSource.Repeat GL_REPEAT both horizontally and vertically
++
++ \note Some OpenGL ES 2 implementations do not support the GL_REPEAT
++ wrap mode with non-power-of-two textures.
++ */
++
++ QQuickShaderEffectSource::WrapMode QQuickShaderEffectSource::wrapMode() const
++ {
++ return m_wrapMode;
++ }
++
++ void QQuickShaderEffectSource::setWrapMode(WrapMode mode)
++ {
++ if (mode == m_wrapMode)
++ return;
++ m_wrapMode = mode;
++ update();
++ emit wrapModeChanged();
++ }
++
++ /*!
++ \qmlproperty Item QtQuick::ShaderEffectSource::sourceItem
++
++ This property holds the item to be rendered into the texture.
++ Setting this to null while \l live is true, will release the texture
++ resources.
++ */
++
++ QQuickItem *QQuickShaderEffectSource::sourceItem() const
++ {
++ return m_sourceItem;
++ }
++
++ void QQuickShaderEffectSource::itemGeometryChanged(QQuickItem *item, QQuickGeometryChange change, const QRectF &)
++ {
++ Q_ASSERT(item == m_sourceItem);
++ Q_UNUSED(item);
++ if (change.sizeChange())
++ update();
++ }
++
++ void QQuickShaderEffectSource::setSourceItem(QQuickItem *item)
++ {
++ if (item == m_sourceItem)
++ return;
++ if (m_sourceItem) {
++ QQuickItemPrivate *d = QQuickItemPrivate::get(m_sourceItem);
++ d->derefFromEffectItem(m_hideSource);
++ d->removeItemChangeListener(this, QQuickItemPrivate::Geometry);
++ disconnect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
++ if (window())
++ d->derefWindow();
++ }
++
++ m_sourceItem = item;
++
++ if (m_sourceItem) {
++ if (window() == m_sourceItem->window()
++ || (window() == nullptr && m_sourceItem->window())
++ || (m_sourceItem->window() == nullptr && window())) {
++ QQuickItemPrivate *d = QQuickItemPrivate::get(item);
++ // 'item' needs a window to get a scene graph node. It usually gets one through its
++ // parent, but if the source item is "inline" rather than a reference -- i.e.
++ // "sourceItem: Item { }" instead of "sourceItem: foo" -- it will not get a parent.
++ // In those cases, 'item' should get the window from 'this'.
++ if (window())
++ d->refWindow(window());
++ else if (m_sourceItem->window())
++ d->refWindow(m_sourceItem->window());
++ d->refFromEffectItem(m_hideSource);
++ d->addItemChangeListener(this, QQuickItemPrivate::Geometry);
++ connect(m_sourceItem, SIGNAL(destroyed(QObject*)), this, SLOT(sourceItemDestroyed(QObject*)));
++ } else {
++ qWarning("ShaderEffectSource: sourceItem and ShaderEffectSource must both be children of the same window.");
++ m_sourceItem = nullptr;
++ }
++ }
++ update();
++ emit sourceItemChanged();
++ }
++
++ void QQuickShaderEffectSource::sourceItemDestroyed(QObject *item)
++ {
++ Q_ASSERT(item == m_sourceItem);
++ Q_UNUSED(item);
++ m_sourceItem = nullptr;
++ update();
++ emit sourceItemChanged();
++ }
++
++
++ /*!
++ \qmlproperty rect QtQuick::ShaderEffectSource::sourceRect
++
++ This property defines which rectangular area of the \l sourceItem to
++ render into the texture. The source rectangle can be larger than
++ \l sourceItem itself. If the rectangle is null, which is the default,
++ the whole \l sourceItem is rendered to texture.
++ */
++
++ QRectF QQuickShaderEffectSource::sourceRect() const
++ {
++ return m_sourceRect;
++ }
++
++ void QQuickShaderEffectSource::setSourceRect(const QRectF &rect)
++ {
++ if (rect == m_sourceRect)
++ return;
++ m_sourceRect = rect;
++ update();
++ emit sourceRectChanged();
++ }
++
++ /*!
++ \qmlproperty size QtQuick::ShaderEffectSource::textureSize
++
++ This property holds the requested pixel size of the texture. If it is
++ empty, which is the default, the size of the source rectangle is used.
++
++ \note This value is in pixels since it directly controls the size of a
++ texture object.
++
++ \note Some platforms have a limit on how small framebuffer objects can be,
++ which means the actual texture size might be larger than the requested
++ size.
++ */
++
++ QSize QQuickShaderEffectSource::textureSize() const
++ {
++ return m_textureSize;
++ }
++
++ void QQuickShaderEffectSource::setTextureSize(const QSize &size)
++ {
++ if (size == m_textureSize)
++ return;
++ m_textureSize = size;
++ update();
++ emit textureSizeChanged();
++ }
++
++ /*!
++ \qmlproperty enumeration QtQuick::ShaderEffectSource::format
++
++ This property defines the format of the backing texture.
++ Modifying this property makes most sense when the item is used as a
++ source texture of a \l ShaderEffect.
++
++ \value ShaderEffectSource.RGBA8
++ \value ShaderEffectSource.RGBA16F
++ \value ShaderEffectSource.RGBA32F
++ \value ShaderEffectSource.Alpha Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
++ \value ShaderEffectSource.RGB Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
++ \value ShaderEffectSource.RGBA Starting with Qt 6.0, this value is not in use and has the same effect as \c RGBA8 in practice.
++ */
++
++ QQuickShaderEffectSource::Format QQuickShaderEffectSource::format() const
++ {
++ return m_format;
++ }
++
++ void QQuickShaderEffectSource::setFormat(QQuickShaderEffectSource::Format format)
++ {
++ if (format == m_format)
++ return;
++ m_format = format;
++ update();
++ emit formatChanged();
++ }
++
++ /*!
++ \qmlproperty bool QtQuick::ShaderEffectSource::live
++
++ If this property is true, the texture is updated whenever the
++ \l sourceItem updates. Otherwise, it will be a frozen image, even if
++ \l sourceItem is assigned a new item. The property is true by default.
++ */
++
++ bool QQuickShaderEffectSource::live() const
++ {
++ return m_live;
++ }
++
++ void QQuickShaderEffectSource::setLive(bool live)
++ {
++ if (live == m_live)
++ return;
++ m_live = live;
++ update();
++ emit liveChanged();
++ }
++
++ /*!
++ \qmlproperty bool QtQuick::ShaderEffectSource::hideSource
++
++ If this property is true, the \l sourceItem is hidden, though it will still
++ be rendered into the texture. As opposed to hiding the \l sourceItem by
++ setting \l{Item::visible}{visible} to false, setting this property to true
++ will not prevent mouse or keyboard input from reaching \l sourceItem.
++ The property is useful when the ShaderEffectSource is anchored on top of,
++ and meant to replace the \l sourceItem.
++ */
++
++ bool QQuickShaderEffectSource::hideSource() const
++ {
++ return m_hideSource;
++ }
++
++ void QQuickShaderEffectSource::setHideSource(bool hide)
++ {
++ if (hide == m_hideSource)
++ return;
++ if (m_sourceItem) {
++ QQuickItemPrivate::get(m_sourceItem)->refFromEffectItem(hide);
++ QQuickItemPrivate::get(m_sourceItem)->derefFromEffectItem(m_hideSource);
++ }
++ m_hideSource = hide;
++ update();
++ emit hideSourceChanged();
++ }
++
++ /*!
++ \qmlproperty bool QtQuick::ShaderEffectSource::mipmap
++
++ If this property is true, mipmaps are generated for the texture.
++
++ \note Some OpenGL ES 2 implementations do not support mipmapping of
++ non-power-of-two textures.
++ */
++
++ bool QQuickShaderEffectSource::mipmap() const
++ {
++ return m_mipmap;
++ }
++
++ void QQuickShaderEffectSource::setMipmap(bool enabled)
++ {
++ if (enabled == m_mipmap)
++ return;
++ m_mipmap = enabled;
++ update();
++ emit mipmapChanged();
++ }
++
++ /*!
++ \qmlproperty bool QtQuick::ShaderEffectSource::recursive
++
++ Set this property to true if the ShaderEffectSource has a dependency on
++ itself. ShaderEffectSources form a dependency chain, where one
++ ShaderEffectSource can be part of the \l sourceItem of another.
++ If there is a loop in this chain, a ShaderEffectSource could end up trying
++ to render into the same texture it is using as source, which is not allowed
++ by OpenGL. When this property is set to true, an extra texture is allocated
++ so that ShaderEffectSource can keep a copy of the texture from the previous
++ frame. It can then render into one texture and use the texture from the
++ previous frame as source.
++
++ Setting both this property and \l live to true will cause the scene graph
++ to render continuously. Since the ShaderEffectSource depends on itself,
++ updating it means that it immediately becomes dirty again.
++ */
++
++ bool QQuickShaderEffectSource::recursive() const
++ {
++ return m_recursive;
++ }
++
++ void QQuickShaderEffectSource::setRecursive(bool enabled)
++ {
++ if (enabled == m_recursive)
++ return;
++ m_recursive = enabled;
++ emit recursiveChanged();
++ }
++
++ /*!
++ \qmlproperty enumeration QtQuick::ShaderEffectSource::textureMirroring
++ \since 5.6
++
++ This property defines how the generated OpenGL texture should be mirrored.
++ The default value is \c{ShaderEffectSource.MirrorVertically}.
++ Custom mirroring can be useful if the generated texture is directly accessed by custom shaders,
++ such as those specified by ShaderEffect. Mirroring has no effect on the UI representation of
++ the ShaderEffectSource item itself.
++
++ \value ShaderEffectSource.NoMirroring No mirroring
++ \value ShaderEffectSource.MirrorHorizontally The generated texture is flipped along X-axis.
++ \value ShaderEffectSource.MirrorVertically The generated texture is flipped along Y-axis.
++ */
++
++ QQuickShaderEffectSource::TextureMirroring QQuickShaderEffectSource::textureMirroring() const
++ {
++ return QQuickShaderEffectSource::TextureMirroring(m_textureMirroring);
++ }
++
++ void QQuickShaderEffectSource::setTextureMirroring(TextureMirroring mirroring)
++ {
++ if (mirroring == QQuickShaderEffectSource::TextureMirroring(m_textureMirroring))
++ return;
++ m_textureMirroring = mirroring;
++ update();
++ emit textureMirroringChanged();
++ }
++
++ /*!
++ \qmlproperty int QtQuick::ShaderEffectSource::samples
++ \since 5.10
++
++ This property allows requesting multisampled rendering.
++
++ By default multisampling is enabled whenever multisampling is enabled for
++ the entire window, assuming the scenegraph renderer in use and the
++ underlying graphics API supports this.
++
++ By setting the value to 2, 4, etc. multisampled rendering can be requested
++ for a part of the scene without enabling multisampling for the entire
++ scene. This way multisampling is applied only to a given subtree, which can
++ lead to significant performance gains since multisampling is not applied to
++ other parts of the scene.
++
++ \note Enabling multisampling can be potentially expensive regardless of the
++ layer's size, as it incurs a hardware and driver dependent performance and
++ memory cost.
++
++ \note This property is only functional when support for multisample
++ renderbuffers and framebuffer blits is available. Otherwise the value is
++ silently ignored.
++ */
++ int QQuickShaderEffectSource::samples() const
++ {
++ return m_samples;
++ }
++
++ void QQuickShaderEffectSource::setSamples(int count)
++ {
++ if (count == m_samples)
++ return;
++ m_samples = count;
++ update();
++ emit samplesChanged();
++ }
++
++ /*!
++ \qmlmethod void QtQuick::ShaderEffectSource::scheduleUpdate()
++
++ Schedules a re-rendering of the texture for the next frame.
++ Use this to update the texture when \l live is false.
++ */
++
++ void QQuickShaderEffectSource::scheduleUpdate()
++ {
++ if (m_grab)
++ return;
++ m_grab = true;
++ update();
++ }
++
++ static void get_wrap_mode(QQuickShaderEffectSource::WrapMode mode, QSGTexture::WrapMode *hWrap, QSGTexture::WrapMode *vWrap)
++ {
++ switch (mode) {
++ case QQuickShaderEffectSource::RepeatHorizontally:
++ *hWrap = QSGTexture::Repeat;
++ *vWrap = QSGTexture::ClampToEdge;
++ break;
++ case QQuickShaderEffectSource::RepeatVertically:
++ *vWrap = QSGTexture::Repeat;
++ *hWrap = QSGTexture::ClampToEdge;
++ break;
++ case QQuickShaderEffectSource::Repeat:
++ *hWrap = *vWrap = QSGTexture::Repeat;
++ break;
++ default:
++ // QQuickShaderEffectSource::ClampToEdge
++ *hWrap = *vWrap = QSGTexture::ClampToEdge;
++ break;
++ }
++ }
++
++
++ void QQuickShaderEffectSource::releaseResources()
++ {
++ if (m_texture || m_provider) {
++ window()->scheduleRenderJob(new QQuickShaderEffectSourceCleanup(m_texture, m_provider),
++ QQuickWindow::AfterSynchronizingStage);
++ m_texture = nullptr;
++ m_provider = nullptr;
++ }
++ }
++
++ class QQuickShaderSourceAttachedNode : public QObject, public QSGNode
++ {
++ Q_OBJECT
++ public:
++ Q_SLOT void markTextureDirty() {
++ QSGNode *pn = QSGNode::parent();
++ if (pn) {
++ Q_ASSERT(pn->type() == QSGNode::GeometryNodeType);
++ pn->markDirty(DirtyMaterial);
++ }
++ }
++ };
++
++ static QSGLayer::Format toLayerFormat(QQuickShaderEffectSource::Format format)
++ {
++ switch (format) {
++ case QQuickShaderEffectSource::RGBA8:
++ return QSGLayer::RGBA8;
++ case QQuickShaderEffectSource::RGBA16F:
++ return QSGLayer::RGBA16F;
++ case QQuickShaderEffectSource::RGBA32F:
++ return QSGLayer::RGBA32F;
++ default:
++ return QSGLayer::RGBA8;
++ }
++ }
++
++ QSGNode *QQuickShaderEffectSource::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeData *)
++ {
++ if (!m_sourceItem || m_sourceItem->width() <= 0 || m_sourceItem->height() <= 0) {
++ if (m_texture)
++ m_texture->setItem(nullptr);
++ delete oldNode;
++ return nullptr;
++ }
++
++ ensureTexture();
++
++ m_texture->setLive(m_live);
++ m_texture->setItem(QQuickItemPrivate::get(m_sourceItem)->itemNode());
++ QRectF sourceRect = m_sourceRect.width() == 0 || m_sourceRect.height() == 0
++ ? QRectF(0, 0, m_sourceItem->width(), m_sourceItem->height())
++ : m_sourceRect;
++ m_texture->setRect(sourceRect);
++ QQuickItemPrivate *d = static_cast<QQuickItemPrivate *>(QObjectPrivate::get(this));
++ const qreal dpr = d->effectiveDevicePixelRatio();
++ QSize textureSize = m_textureSize.isEmpty()
++ ? (QSizeF(qAbs(sourceRect.width()), qAbs(sourceRect.height())) * dpr)
++ .toSize().expandedTo(QSize(1, 1))
++ : m_textureSize;
++
++ const QSize minTextureSize = d->sceneGraphContext()->minimumFBOSize();
++ // Keep power-of-two by doubling the size.
++ while (textureSize.width() < minTextureSize.width())
++ textureSize.rwidth() *= 2;
++ while (textureSize.height() < minTextureSize.height())
++ textureSize.rheight() *= 2;
++
++ m_texture->setDevicePixelRatio(d->effectiveDevicePixelRatio());
++ m_texture->setSize(textureSize);
++ m_texture->setRecursive(m_recursive);
++ m_texture->setFormat(toLayerFormat(m_format));
++ m_texture->setHasMipmaps(m_mipmap);
++ m_texture->setMirrorHorizontal(m_textureMirroring & MirrorHorizontally);
++ m_texture->setMirrorVertical(m_textureMirroring & MirrorVertically);
++ m_texture->setSamples(m_samples);
++
++ if (m_grab)
++ m_texture->scheduleUpdate();
++ m_grab = false;
++
++ QSGTexture::Filtering filtering = QQuickItemPrivate::get(this)->smooth
++ ? QSGTexture::Linear
++ : QSGTexture::Nearest;
++ QSGTexture::Filtering mmFiltering = m_mipmap ? filtering : QSGTexture::None;
++ QSGTexture::WrapMode hWrap, vWrap;
++ get_wrap_mode(m_wrapMode, &hWrap, &vWrap);
++
++ if (m_provider) {
++ m_provider->mipmapFiltering = mmFiltering;
++ m_provider->filtering = filtering;
++ m_provider->horizontalWrap = hWrap;
++ m_provider->verticalWrap = vWrap;
++ }
++
++ // Don't create the paint node if we're not spanning any area
++ if (width() <= 0 || height() <= 0) {
++ delete oldNode;
++ return nullptr;
++ }
++
++ QSGInternalImageNode *node = static_cast<QSGInternalImageNode *>(oldNode);
++ if (!node) {
++ node = d->sceneGraphContext()->createInternalImageNode(d->sceneGraphRenderContext());
++ node->setFlag(QSGNode::UsePreprocess);
++ node->setTexture(m_texture);
++ QQuickShaderSourceAttachedNode *attached = new QQuickShaderSourceAttachedNode;
++ node->appendChildNode(attached);
++ connect(m_texture, SIGNAL(updateRequested()), attached, SLOT(markTextureDirty()));
++ }
++
++ // If live and recursive, update continuously.
++ if (m_live && m_recursive)
++ node->markDirty(QSGNode::DirtyMaterial);
++
++ node->setMipmapFiltering(mmFiltering);
++ node->setFiltering(filtering);
++ node->setHorizontalWrapMode(hWrap);
++ node->setVerticalWrapMode(vWrap);
++
++ const qreal dx = x() * dpr;
++ const qreal dy = y() * dpr;
++ const QSizeF devicePixelSize(qCeil(dx - std::floor(dx)) + qMax(1, qRound(width() * dpr)),
++ qCeil(dy - std::floor(dy)) + qMax(1, qRound(height() * dpr)));
++ const QRectF targetRect(QPointF(0, 0), devicePixelSize / dpr);
++ node->setTargetRect(targetRect);
++ node->setInnerTargetRect(targetRect);
++
++ node->update();
++
++ return node;
++ }
++
++ void QQuickShaderEffectSource::invalidateSceneGraph()
++ {
++ if (m_texture)
++ delete m_texture;
++ if (m_provider)
++ delete m_provider;
++ m_texture = nullptr;
++ m_provider = nullptr;
++ }
++
++ void QQuickShaderEffectSource::itemChange(ItemChange change, const ItemChangeData &value)
++ {
++ if (change == QQuickItem::ItemSceneChange && m_sourceItem) {
++ // See comment in QQuickShaderEffectSource::setSourceItem().
++ if (value.window)
++ QQuickItemPrivate::get(m_sourceItem)->refWindow(value.window);
++ else
++ QQuickItemPrivate::get(m_sourceItem)->derefWindow();
++ }
++ QQuickItem::itemChange(change, value);
++ }
++
++ QT_END_NAMESPACE
++
++ #include "qquickshadereffectsource.moc"
++ #include "moc_qquickshadereffectsource_p.cpp"