Commit 42d6acd0 authored by Gunnar Sletta's avatar Gunnar Sletta Committed by Qt by Nokia

Introduce layerering support (ShaderEffectSource) directly in Item

This is enabled by doing "Item.layer.enabled: true". The implementation
is solely based on the existing shader effect (source) and simply
swaps in a sibling next to the item when enabled.

This change also adds the QSGTextureProvider to the public API,
as it is now a natural part of the QQuickItem API since all items
can be textures.

Change-Id: I26705c11e92d5623a5121300acc123782b784077
Reviewed-by: default avatarKim M. Kalland <kim.kalland@nokia.com>
parent a9b103d0
......@@ -42,7 +42,7 @@
#include "qquickimage_p.h"
#include "qquickimage_p_p.h"
#include <QtQuick/private/qsgtextureprovider_p.h>
#include <QtQuick/qsgtextureprovider.h>
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qsgadaptationlayer_p.h>
......
......@@ -44,7 +44,7 @@
#define QQUICKIMAGE_P_H
#include "qquickimagebase_p.h"
#include <QtQuick/private/qsgtextureprovider_p.h>
#include <QtQuick/qsgtextureprovider.h>
QT_BEGIN_HEADER
......
This diff is collapsed.
......@@ -81,6 +81,7 @@ private:
Q_DECLARE_PRIVATE(QQuickTransform)
};
class QQuickItemLayer;
class QDeclarativeV8Function;
class QDeclarativeState;
class QQuickAnchorLine;
......@@ -145,6 +146,8 @@ class Q_QUICK_EXPORT QQuickItem : public QObject, public QDeclarativeParserStatu
Q_PROPERTY(qreal implicitWidth READ implicitWidth WRITE setImplicitWidth NOTIFY implicitWidthChanged)
Q_PROPERTY(qreal implicitHeight READ implicitHeight WRITE setImplicitHeight NOTIFY implicitHeightChanged)
Q_PRIVATE_PROPERTY(QQuickItem::d_func(), QQuickItemLayer *layer READ layer DESIGNABLE false CONSTANT FINAL)
Q_ENUMS(TransformOrigin)
Q_CLASSINFO("DefaultProperty", "data")
Q_CLASSINFO("qt_HasQmlAccessors", "true")
......@@ -316,8 +319,8 @@ public:
UpdatePaintNodeData();
};
virtual bool isTextureProvider() const { return false; }
virtual QSGTextureProvider *textureProvider() const { return 0; }
virtual bool isTextureProvider() const;
virtual QSGTextureProvider *textureProvider() const;
public Q_SLOTS:
void update();
......
......@@ -78,6 +78,9 @@
#include <QtCore/qdebug.h>
#include <QtCore/qelapsedtimer.h>
#include <QtQuick/private/qquickshadereffectsource_p.h>
#include <QtQuick/private/qquickshadereffect_p.h>
QT_BEGIN_NAMESPACE
class QNetworkReply;
......@@ -134,6 +137,101 @@ public:
QList<QQuickItem *> items;
};
class QQuickItemLayer : public QObject, public QQuickItemChangeListener
{
Q_OBJECT
Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged)
Q_PROPERTY(QSize textureSize READ size WRITE setSize NOTIFY sizeChanged)
Q_PROPERTY(QRectF sourceRect READ sourceRect WRITE setSourceRect NOTIFY sourceRectChanged)
Q_PROPERTY(bool mipmap READ mipmap WRITE setMipmap NOTIFY mipmapChanged)
Q_PROPERTY(bool smooth READ smooth WRITE setSmooth NOTIFY smoothChanged)
Q_PROPERTY(QQuickShaderEffectSource::WrapMode wrapMode READ wrapMode WRITE setWrapMode NOTIFY wrapModeChanged)
Q_PROPERTY(QQuickShaderEffectSource::Format format READ format WRITE setFormat NOTIFY formatChanged)
Q_PROPERTY(QString samplerName READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(QDeclarativeComponent *effect READ effect WRITE setEffect NOTIFY effectChanged)
public:
QQuickItemLayer(QQuickItem *item);
~QQuickItemLayer();
void classBegin();
void componentComplete();
bool enabled() const { return m_effectSource != 0; }
void setEnabled(bool enabled);
bool mipmap() const { return m_mipmap; }
void setMipmap(bool mipmap);
bool smooth() const { return m_smooth; }
void setSmooth(bool s);
QSize size() const { return m_size; }
void setSize(const QSize &size);
QQuickShaderEffectSource::Format format() const { return m_format; }
void setFormat(QQuickShaderEffectSource::Format f);
QRectF sourceRect() const { return m_sourceRect; }
void setSourceRect(const QRectF &sourceRect);
QQuickShaderEffectSource::WrapMode wrapMode() const { return m_wrapMode; }
void setWrapMode(QQuickShaderEffectSource::WrapMode mode);
QString name() const { return m_name; }
void setName(const QString &name) {
if (m_name == name)
return;
m_name = name;
emit nameChanged(name);
}
QDeclarativeComponent *effect() const { return m_effectComponent; }
void setEffect(QDeclarativeComponent *effect);
QQuickShaderEffectSource *effectSource() const { return m_effectSource; }
void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &);
void itemOpacityChanged(QQuickItem *);
void itemParentChanged(QQuickItem *, QQuickItem *);
void itemSiblingOrderChanged(QQuickItem *);
void itemVisibilityChanged(QQuickItem *);
void updateMatrix();
void updateGeometry();
void updateOpacity();
void updateZ();
signals:
void enabledChanged(bool enabled);
void sizeChanged(const QSize &size);
void mipmapChanged(bool mipmap);
void wrapModeChanged(QQuickShaderEffectSource::WrapMode mode);
void nameChanged(const QString &name);
void effectChanged(QDeclarativeComponent *component);
void smoothChanged(bool smooth);
void formatChanged(QQuickShaderEffectSource::Format format);
void sourceRectChanged(const QRectF &sourceRect);
private:
void activate();
void deactivate();
QQuickItem *m_item;
bool m_enabled;
bool m_mipmap;
bool m_smooth;
bool m_componentComplete;
QQuickShaderEffectSource::WrapMode m_wrapMode;
QQuickShaderEffectSource::Format m_format;
QSize m_size;
QRectF m_sourceRect;
QString m_name;
QDeclarativeComponent *m_effectComponent;
QQuickShaderEffect *m_effect;
QQuickShaderEffectSource *m_effectSource;
};
class Q_QUICK_EXPORT QQuickItemPrivate : public QObjectPrivate
{
Q_DECLARE_PUBLIC(QQuickItem)
......@@ -166,6 +264,8 @@ public:
QQuickAnchorLine verticalCenter() const;
QQuickAnchorLine baseline() const;
QQuickItemLayer *layer() const;
// data property
static void data_append(QDeclarativeListProperty<QObject> *, QObject *);
static int data_count(QDeclarativeListProperty<QObject> *);
......@@ -407,7 +507,7 @@ public:
TransformUpdateMask = TransformOrigin | Transform | BasicTransform | Position | Size | Canvas,
ComplexTransformUpdateMask = Transform | Canvas,
ContentUpdateMask = Size | Content | Smooth | Canvas,
ChildrenUpdateMask = ChildrenChanged | ChildrenStackingChanged | EffectReference | Canvas,
ChildrenUpdateMask = ChildrenChanged | ChildrenStackingChanged | EffectReference | Canvas
};
quint32 dirtyAttributes;
......@@ -453,6 +553,8 @@ public:
QQuickScreenAttached *screenAttached;
mutable QQuickItemLayer *_layer;
static qint64 consistentTime;
static void setConsistentTime(qint64 t);
static void start(QElapsedTimer &);
......@@ -755,6 +857,7 @@ Q_DECLARE_OPERATORS_FOR_FLAGS(QQuickItemPrivate::ChangeTypes);
QT_END_NAMESPACE
QML_DECLARE_TYPE(QQuickItemLayer)
QML_DECLARE_TYPE(QQuickKeysAttached)
QML_DECLARE_TYPEINFO(QQuickKeysAttached, QML_HAS_ATTACHED_PROPERTIES)
QML_DECLARE_TYPE(QQuickKeyNavigationAttached)
......
......@@ -64,7 +64,7 @@ class QQuickAnchorsPrivate;
class QQuickItemChangeListener
{
public:
virtual void itemGeometryChanged(QQuickItem *, const QRectF &, const QRectF &) {}
virtual void itemGeometryChanged(QQuickItem *, const QRectF & /* new */, const QRectF & /* old */ ) {}
virtual void itemSiblingOrderChanged(QQuickItem *) {}
virtual void itemVisibilityChanged(QQuickItem *) {}
virtual void itemOpacityChanged(QQuickItem *) {}
......
......@@ -163,6 +163,7 @@ static void qt_quickitems_defineModule(const char *uri, int major, int minor)
qmlRegisterType<QQuickVisualDataGroup>(uri,major,minor,"VisualDataGroup");
qmlRegisterType<QQuickVisualItemModel>(uri,major,minor,"VisualItemModel");
qmlRegisterType<QQuickItemLayer>();
qmlRegisterType<QQuickAnchors>();
qmlRegisterType<QQuickKeyEvent>();
qmlRegisterType<QQuickMouseEvent>();
......
......@@ -46,7 +46,7 @@
#include "qquickitem_p.h"
#include <QtQuick/private/qsgcontext_p.h>
#include <QtQuick/private/qsgtextureprovider_p.h>
#include <QtQuick/qsgtextureprovider.h>
#include "qquickcanvas.h"
#include "qquickimage_p.h"
......@@ -190,6 +190,7 @@ QQuickShaderEffect::QQuickShaderEffect(QQuickItem *parent)
, m_programDirty(true)
, m_dirtyMesh(true)
, m_dirtyGeometry(true)
, m_complete(false)
{
setFlag(QQuickItem::ItemHasContents);
}
......@@ -199,12 +200,6 @@ QQuickShaderEffect::~QQuickShaderEffect()
reset();
}
void QQuickShaderEffect::componentComplete()
{
updateProperties();
QQuickItem::componentComplete();
}
/*!
\qmlproperty string QtQuick2::ShaderEffect::fragmentShader
......@@ -218,11 +213,8 @@ void QQuickShaderEffect::setFragmentShader(const QByteArray &code)
if (m_source.fragmentCode.constData() == code.constData())
return;
m_source.fragmentCode = code;
if (isComponentComplete()) {
reset();
updateProperties();
update();
}
update();
m_complete = false;
emit fragmentShaderChanged();
}
......@@ -240,11 +232,8 @@ void QQuickShaderEffect::setVertexShader(const QByteArray &code)
if (m_source.vertexCode.constData() == code.constData())
return;
m_source.vertexCode = code;
if (isComponentComplete()) {
reset();
updateProperties();
update();
}
update();
m_complete = false;
emit vertexShaderChanged();
}
......@@ -425,6 +414,10 @@ void QQuickShaderEffect::connectPropertySignals()
signalName.append(mp.notifySignal().signature());
connect(this, signalName, this, SLOT(updateData()));
} else {
// If the source is set via a dynamic property, like the layer is, then we need this check
// to disable the warning.
if (property(it->constData()).isValid())
continue;
qWarning("QQuickShaderEffect: '%s' does not have a matching property!", it->constData());
}
}
......@@ -439,11 +432,26 @@ void QQuickShaderEffect::connectPropertySignals()
source.mapper->setMapping(this, i);
connect(source.mapper, SIGNAL(mapped(int)), this, SLOT(changeSource(int)));
} else {
// If the source is set via a dynamic property, like the layer is, then we need this check
// to disable the warning.
if (property(source.name.constData()).isValid())
continue;
qWarning("QQuickShaderEffect: '%s' does not have a matching source!", source.name.constData());
}
}
}
void QQuickShaderEffect::ensureCompleted()
{
if (!m_complete) {
reset();
updateProperties();
m_complete = true;
}
}
void QQuickShaderEffect::reset()
{
disconnectPropertySignals();
......@@ -666,6 +674,8 @@ QSGNode *QQuickShaderEffect::updatePaintNode(QSGNode *oldNode, UpdatePaintNodeDa
{
QQuickShaderEffectNode *node = static_cast<QQuickShaderEffectNode *>(oldNode);
ensureCompleted();
// In the case of a bad vertex shader, don't try to create a node...
if (m_source.attributeNames.isEmpty()) {
if (node)
......
......@@ -83,8 +83,6 @@ public:
QQuickShaderEffect(QQuickItem *parent = 0);
~QQuickShaderEffect();
virtual void componentComplete();
QByteArray fragmentShader() const { return m_source.fragmentCode; }
void setFragmentShader(const QByteArray &code);
......@@ -100,6 +98,8 @@ public:
CullMode cullMode() const { return m_cullMode; }
void setCullMode(CullMode face);
void ensureCompleted();
Q_SIGNALS:
void fragmentShaderChanged();
void vertexShaderChanged();
......@@ -148,6 +148,8 @@ private:
uint m_programDirty : 1;
uint m_dirtyMesh : 1;
uint m_dirtyGeometry : 1;
uint m_complete : 1;
};
QT_END_NAMESPACE
......
......@@ -42,7 +42,7 @@
#include <private/qquickshadereffectnode_p.h>
#include "qquickshadereffectmesh_p.h"
#include <QtQuick/private/qsgtextureprovider_p.h>
#include <QtQuick/qsgtextureprovider.h>
#include <QtQuick/private/qsgrenderer_p.h>
QT_BEGIN_NAMESPACE
......
......@@ -44,7 +44,7 @@
#include <QtQuick/qsgnode.h>
#include <QtQuick/qsgmaterial.h>
#include <QtQuick/private/qsgtextureprovider_p.h>
#include <QtQuick/qsgtextureprovider.h>
#include <QtQuick/qquickitem.h>
#include <QtCore/qsharedpointer.h>
......
......@@ -43,7 +43,7 @@
#define QQUICKSHADEREFFECTSOURCE_P_H
#include "qquickitem.h"
#include <QtQuick/private/qsgtextureprovider_p.h>
#include <QtQuick/qsgtextureprovider.h>
#include <private/qsgadaptationlayer_p.h>
#include <QtQuick/private/qsgcontext_p.h>
#include <private/qsgdefaultimagenode_p.h>
......
......@@ -41,7 +41,7 @@
#include "qsgdefaultimagenode_p.h"
#include <QtQuick/private/qsgtextureprovider_p.h>
#include <QtQuick/qsgtextureprovider.h>
#include <QtCore/qvarlengtharray.h>
#include <QtCore/qmath.h>
......
......@@ -32,7 +32,7 @@ HEADERS += \
$$PWD/util/qsgvertexcolormaterial.h \
$$PWD/util/qsgtexture.h \
$$PWD/util/qsgtexture_p.h \
$$PWD/util/qsgtextureprovider_p.h \
$$PWD/util/qsgtextureprovider.h \
$$PWD/util/qsgpainternode_p.h \
$$PWD/util/qsgdistancefieldutil_p.h
......
......@@ -39,11 +39,7 @@
**
****************************************************************************/
#include "qsgtextureprovider_p.h"
#ifndef GL_CLAMP_TO_EDGE
#define GL_CLAMP_TO_EDGE 0x812F
#endif
#include "qsgtextureprovider.h"
QT_BEGIN_NAMESPACE
......
import QtQuick 2.0
Item
{
width: 100
height: 100
Rectangle {
id: box
width: 100
height: 100
color: "#0000ff"
Rectangle {
x: 50
width: 50
height: 100
color: "#00ff00"
}
layer.enabled: true
layer.effect: ShaderEffect {
fragmentShader: "
uniform lowp sampler2D source;
uniform lowp float qt_Opacity;
varying highp vec2 qt_TexCoord0;
void main() {
gl_FragColor = texture2D(source, qt_TexCoord0).bgra * qt_Opacity;
}"
}
}
}
import QtQuick 2.0
Item
{
width: 200
height: 200
Item {
width: 20
height: 20
scale: 10
layer.enabled: true
anchors.centerIn: parent
Rectangle {
width: 20
height: 20
gradient: Gradient {
GradientStop { position: 0; color: "white" }
GradientStop { position: 1; color: "black" }
}
}
}
}
import QtQuick 2.0
Item
{
width: 100
height: 100
Rectangle {
id: box
width: 600
height: 600
scale: 1 / 6.
color: "black"
layer.enabled: true
layer.mipmap: true
layer.smooth: true
anchors.centerIn: parent
Rectangle {
x: 1
width: 1
height: parent.height
color: "white"
}
}
}
import QtQuick 2.0
Item {
width: 200
height: 100
Row {
id: layerRoot
width: 20
height: 10
Rectangle { width: 10; height: 10; color: "red" }
Rectangle { width: 10; height: 10; color: "blue" }
layer.enabled: true
layer.smooth: true
anchors.centerIn: parent
scale: 10
}
}
import QtQuick 2.0
Item
{
width: 100
height: 100
Rectangle {
id: box
width: 100
height: 100
color: "#ff0000"
layer.enabled: true
layer.sourceRect: Qt.rect(-10, -10, box.width + 20, box.height + 20);
// A shader that pads the transparent pixels with blue.
layer.effect: ShaderEffect {
fragmentShader: "
uniform lowp sampler2D source;
uniform lowp float qt_Opacity;
varying highp vec2 qt_TexCoord0;
void main() {
vec4 c = texture2D(source, qt_TexCoord0);
if (c.a == 0.)
c = vec4(0, 0, 1, 1);
gl_FragColor = c * qt_Opacity;
}
"
}
}
}
import QtQuick 2.0
Item
{
width: 100
height: 100
Rectangle {
id: box
width: 100
height: 100
color: "#0000ff"
Rectangle {
x: 50
width: 50
height: 100
color: "#00ff00"
}
visible: false
layer.enabled: true
}
ShaderEffect {
anchors.fill: parent
property variant source: box
fragmentShader: "
uniform lowp sampler2D source;
uniform lowp float qt_Opacity;
varying highp vec2 qt_TexCoord0;
void main() {
gl_FragColor = texture2D(source, qt_TexCoord0).bgra * qt_Opacity;
}"
}
}
import QtQuick 2.0
Item
{
id: root
width: 100
height: 100
property bool layerEffect: false;
onLayerEffectChanged: root.maybeUse();
Component.onCompleted: root.maybeUse();
property real layerOpacity: 1;
property bool layerVisible: true;
function maybeUse() {
if (root.layerEffect)
box.layer.effect = shaderEffect
}
Component {
id: shaderEffect
ShaderEffect {
fragmentShader: "
uniform lowp sampler2D source;
uniform lowp float qt_Opacity;
varying highp vec2 qt_TexCoord0;
void main() {
gl_FragColor = texture2D(source, qt_TexCoord0).bgra * qt_Opacity;
}
"
}
}
Rectangle {
id: box
width: 100
height: 100
color: "#0000ff"
visible: parent.layerVisible;
opacity: parent.layerOpacity;
Rectangle {
x: 50
width: 50
height: 100
color: "#00ff00"
}
layer.enabled: true
}
}
import QtQuick 2.0
Item
{
id: root
width: 200
height: 200
Component {
id: shaderEffect
ShaderEffect { }
}
property bool layerEffect: false;
onLayerEffectChanged: root.maybeUse();
Component.onCompleted: root.maybeUse();
function maybeUse() {
if (root.layerEffect)
box.layer.effect = shaderEffect
}
Rectangle {
color: "red"
anchors.left: parent.left
anchors.top: parent.top
width: 100
height: 100
z: 1
}
Rectangle {
color: "#00ff00"
anchors.bottom: parent.bottom
anchors.right: parent.right
width: 100
height: 100
z: 3
}
Rectangle {
id: box
color: "blue"
anchors.fill: parent
anchors.margins: 10
layer.enabled: true
z: 2
}
}
CONFIG += testcase
TARGET = tst_qquickitemlayer
SOURCES += tst_qquickitemlayer.cpp
macx:CONFIG -= app_bundle
testDataFiles.files = data
testDataFiles.path = .
DEPLOYMENT += testDataFiles
include(../../shared/util.pri)
CONFIG += parallel_test
QT += core-private gui-private v8-private declarative-private quick-private testlib
OTHER_FILES += \