shadows added to playground

This commit is contained in:
Uwe Rathmann 2020-10-06 15:49:46 +02:00
parent 7b59793054
commit bbbc02f122
10 changed files with 653 additions and 0 deletions

View File

@ -5,6 +5,7 @@ SUBDIRS += \
dialogbuttons \
invoker \
inputpanel \
shadows \
images
qtHaveModule(webengine) {

View File

@ -0,0 +1,275 @@
#include "BoxShadowNode.h"
#include "QskBoxShapeMetrics.h"
#include <QColor>
#include <QSGMaterialShader>
#include <QSGMaterial>
#include <private/qsgnode_p.h>
namespace
{
class Shader final : public QSGMaterialShader
{
public:
Shader();
char const *const *attributeNames() const override;
void initialize() override;
void updateState( const QSGMaterialShader::RenderState &state,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial) override;
private:
int m_matrixLocation = -1;
int m_opacityLocation = -1;
int m_aspectLocation = -1;
int m_extentLocation = -1;
int m_radiusLocation = -1;
int m_colorLocation = -1;
int m_offsetLocation = -1;
};
class Material final : public QSGMaterial
{
public:
Material();
QSGMaterialShader* createShader() const override;
QSGMaterialType* type() const override;
int compare( const QSGMaterial* other ) const override;
QVector2D aspect = QVector2D{1.0, 1.0};
float extent = 0.0;
QVector4D radius = QVector4D{0.0, 0.0, 0.0, 0.0};
QColor color = Qt::black;
QVector2D offset;
};
Shader::Shader()
{
const QString root( ":/qskinny/shaders/" );
setShaderSourceFile( QOpenGLShader::Vertex, root + "boxshadow.vert" );
setShaderSourceFile( QOpenGLShader::Fragment, root + "boxshadow.frag" );
}
char const* const* Shader::attributeNames() const
{
static char const *const names[] = { "in_vertex", "in_coord", nullptr };
return names;
}
void Shader::initialize()
{
QSGMaterialShader::initialize();
auto p = program();
m_matrixLocation = p->uniformLocation( "matrix" );
m_aspectLocation = p->uniformLocation( "aspect" );
m_opacityLocation = p->uniformLocation( "opacity" );
m_extentLocation = p->uniformLocation( "extent" );
m_offsetLocation = p->uniformLocation( "offset" );
m_radiusLocation = p->uniformLocation( "radius" );
m_colorLocation = p->uniformLocation( "color" );
}
void Shader::updateState( const QSGMaterialShader::RenderState &state,
QSGMaterial *newMaterial, QSGMaterial *oldMaterial )
{
auto p = program();
if ( state.isMatrixDirty() )
p->setUniformValue(m_matrixLocation, state.combinedMatrix());
if ( state.isOpacityDirty() )
p->setUniformValue(m_opacityLocation, state.opacity());
if ( oldMaterial == nullptr || newMaterial->compare( oldMaterial ) != 0
|| state.isCachedMaterialDataDirty( ))
{
auto material = static_cast< const Material* >(newMaterial);
p->setUniformValue( m_aspectLocation, material->aspect );
p->setUniformValue( m_extentLocation, material->extent );
p->setUniformValue( m_radiusLocation, material->radius );
p->setUniformValue( m_colorLocation, material->color );
p->setUniformValue( m_offsetLocation, material->offset );
}
}
Material::Material()
{
setFlag( QSGMaterial::Blending, true );
}
QSGMaterialShader* Material::createShader() const
{
return new Shader();
}
QSGMaterialType* Material::type() const
{
static QSGMaterialType staticType;
return &staticType;
}
int Material::compare( const QSGMaterial* other ) const
{
auto material = static_cast< const Material* >( other );
if ( material->color == color
&& material->offset == offset
&& material->aspect == aspect
&& qFuzzyCompare(material->extent, extent)
&& qFuzzyCompare(material->radius, radius) )
{
return 0;
}
return QSGMaterial::compare(other);
}
}
class BoxShadowNodePrivate final : public QSGGeometryNodePrivate
{
public:
BoxShadowNodePrivate()
: geometry( QSGGeometry::defaultAttributes_TexturedPoint2D(), 4 )
{
}
QSGGeometry geometry;
Material material;
QRectF rect;
};
BoxShadowNode::BoxShadowNode()
: QSGGeometryNode( *new BoxShadowNodePrivate )
{
Q_D( BoxShadowNode );
setGeometry( &d->geometry );
setMaterial( &d->material );
}
BoxShadowNode::~BoxShadowNode()
{
}
void BoxShadowNode::setRect( const QRectF& rect )
{
Q_D( BoxShadowNode );
if ( rect == d->rect)
return;
d->rect = rect;
QVector2D newAspect( 1.0, 1.0 );
if ( rect.width() >= rect.height() )
newAspect.setX( rect.width() / rect.height());
else
newAspect.setY( rect.height() / rect.width() );
if ( d->material.aspect != newAspect)
{
d->material.aspect = newAspect;
markDirty( QSGNode::DirtyMaterial );
}
}
void BoxShadowNode::setShape( const QskBoxShapeMetrics& shape )
{
Q_D( BoxShadowNode );
const float t = 0.5 * std::min( d->rect.width(), d->rect.height());
const float r1 = shape.radius( Qt::BottomRightCorner ).width();
const float r2 = shape.radius( Qt::TopRightCorner ).width();
const float r3 = shape.radius( Qt::BottomLeftCorner ).width();
const float r4 = shape.radius( Qt::TopLeftCorner ).width();
const auto uniformRadius = QVector4D(
std::min( r1 / t, 1.0f ), std::min( r2 / t, 1.0f ),
std::min( r3 / t, 1.0f ), std::min( r4 / t, 1.0f ) );
if ( d->material.radius != uniformRadius )
{
d->material.radius = uniformRadius;
markDirty( QSGNode::DirtyMaterial );
}
}
void BoxShadowNode::setColor( const QColor& color )
{
Q_D( BoxShadowNode );
const auto a = color.alphaF();
const auto c = QColor::fromRgbF(
color.redF() * a, color.greenF() * a, color.blueF() * a, a );
if ( d->material.color != c )
{
d->material.color = c;
markDirty(QSGNode::DirtyMaterial);
}
}
void BoxShadowNode::setShadow( qreal extent, qreal dx, qreal dy )
{
Q_D( BoxShadowNode );
if ( extent <= 0.0 )
extent = 0.0;
const auto minDimension = std::min( d->rect.width(), d->rect.height() );
const float uniformExtent = ( extent / minDimension ) * 2.0;
if ( !qFuzzyCompare( d->material.extent, uniformExtent ) )
{
d->material.extent = uniformExtent;
markDirty(QSGNode::DirtyMaterial);
}
const auto uniformOffset = QVector2D( dx, dy ) / minDimension;
if ( d->material.offset != uniformOffset)
{
d->material.offset = uniformOffset;
markDirty( QSGNode::DirtyMaterial );
}
}
void BoxShadowNode::updateGeometry()
{
Q_D( BoxShadowNode );
const auto sz = d->material.extent;
const auto aspect = d->material.aspect;
auto rect = d->rect.adjusted(
-sz * aspect.x(), -sz * aspect.y(),
sz * aspect.x(), sz * aspect.y()
);
auto offsetLength = d->material.offset.length();
rect = rect.adjusted(
-offsetLength * aspect.x(), -offsetLength * aspect.y(),
offsetLength * aspect.x(), offsetLength * aspect.y() );
QSGGeometry::updateTexturedRectGeometry(
&d->geometry, rect, QRectF( 0.0, 0.0, 1.0, 1.0 ) );
markDirty( QSGNode::DirtyGeometry );
}

View File

@ -0,0 +1,28 @@
#ifndef BOX_SHADOW_NODE_H
#define BOX_SHADOW_NODE_H
#include <QSGGeometryNode>
class QColor;
class QskBoxShapeMetrics;
class BoxShadowNodePrivate;
class BoxShadowNode : public QSGGeometryNode
{
public:
BoxShadowNode();
~BoxShadowNode() override;
void setRect( const QRectF& );
void setShape( const QskBoxShapeMetrics& );
void setColor( const QColor& );
void setShadow( qreal extent, qreal dx, qreal dy );
void updateGeometry();
private:
Q_DECLARE_PRIVATE( BoxShadowNode )
};
#endif

View File

@ -0,0 +1,138 @@
#include "ShadowedBox.h"
#include "BoxShadowNode.h"
#include <QskBoxNode.h>
#include <QskBoxBorderMetrics.h>
#include <QskBoxBorderColors.h>
#include <QskGradient.h>
#include <QskSkinlet.h>
namespace
{
class Skinlet : public QskSkinlet
{
public:
enum NodeRole { ShadowRole, PanelRole };
Skinlet()
{
setNodeRoles( { ShadowRole, PanelRole } );
}
QRectF subControlRect( const QskSkinnable*,
const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const override
{
if ( subControl == ShadowedBox::Panel )
{
return contentsRect;
}
return QRectF();
}
QSGNode* updateSubNode( const QskSkinnable* skinnable,
quint8 nodeRole, QSGNode* node ) const override
{
const auto box = static_cast< const ShadowedBox* >( skinnable );
switch ( nodeRole )
{
case ShadowRole:
{
auto shadowNode = static_cast< BoxShadowNode* >( node );
if ( shadowNode == nullptr )
shadowNode = new BoxShadowNode();
const auto& s = box->shadow();
const qreal dx = s.extent + s.xOffset;
const qreal dy = s.extent + s.yOffset;
auto r = box->subControlRect( ShadowedBox::Panel );
r.adjust( -dx, -dy, dx, dy );
shadowNode->setRect( r );
shadowNode->setShape( box->shape() );
shadowNode->setShadow( s.extent, s.xOffset, s.yOffset );
shadowNode->setColor( box->shadowColor() );
shadowNode->updateGeometry();
return shadowNode;
}
case PanelRole:
{
auto boxNode = static_cast< QskBoxNode* >( node );
if ( boxNode == nullptr )
boxNode = new QskBoxNode();
const QRectF r = box->subControlRect( ShadowedBox::Panel );
boxNode->setBoxData( r, box->shape(), QskBoxBorderMetrics(),
QskBoxBorderColors(), box->gradient() );
return boxNode;
}
}
return nullptr;
}
};
}
QSK_SUBCONTROL( ShadowedBox, Panel )
ShadowedBox::ShadowedBox(QQuickItem *parentItem)
: QskControl( parentItem )
{
setFlag( QQuickItem::ItemHasContents, true );
setSkinlet( new Skinlet() );
}
ShadowedBox::~ShadowedBox()
{
}
void ShadowedBox::setShadow( const Shadow& shadow )
{
m_shadow = shadow;
update();
}
const ShadowedBox::Shadow& ShadowedBox::shadow() const
{
return m_shadow;
}
void ShadowedBox::setShadowColor( const QColor& color )
{
m_shadowColor = color;
update();
}
QColor ShadowedBox::shadowColor() const
{
return m_shadowColor;
}
void ShadowedBox::setGradient( const QskGradient& gradient )
{
m_gradient = gradient;
update();
}
const QskGradient& ShadowedBox::gradient() const
{
return m_gradient;
}
void ShadowedBox::setShape( const QskBoxShapeMetrics& shape )
{
m_shape = shape;
update();
}
const QskBoxShapeMetrics& ShadowedBox::shape() const
{
return m_shape;
}
#include "moc_ShadowedBox.cpp"

View File

@ -0,0 +1,46 @@
#ifndef SHADOWED_BOX_H
#define SHADOWED_BOX_H
#include <QskControl.h>
#include <QskBoxShapeMetrics.h>
class QskGradient;
class ShadowedBox : public QskControl
{
Q_OBJECT
public:
QSK_SUBCONTROLS( Panel )
class Shadow
{
public:
qreal extent = 0.0;
qreal xOffset = 0.0;
qreal yOffset = 0.0;
};
ShadowedBox(QQuickItem *parent = nullptr);
~ShadowedBox() override;
void setShadow( const Shadow& );
const Shadow& shadow() const;
void setGradient( const QskGradient& );
const QskGradient& gradient() const;
void setShadowColor( const QColor& );
QColor shadowColor() const;
void setShape( const QskBoxShapeMetrics& );
const QskBoxShapeMetrics& shape() const;
private:
Shadow m_shadow;
QColor m_shadowColor = Qt::black;
QskGradient m_gradient;
QskBoxShapeMetrics m_shape;
};
#endif

View File

@ -0,0 +1,72 @@
/*
Heavily inspired by code from the kirigami project:
Copyright 2020 Arjen Hiemstra <ahiemstra@heimr.nl>
License: LGPL-2.0-or-later
https://iquilezles.org/www/articles/distfunctions2d/distfunctions2d.htm
Copyright: 2017 Inigo Quilez
License: MIT
*/
uniform lowp float opacity;
uniform lowp float extent;
uniform lowp vec4 radius;
uniform lowp vec4 color;
uniform lowp vec2 offset;
uniform lowp vec2 aspect;
varying lowp vec2 coord;
lowp float effectiveRadius( in lowp vec4 radii, in lowp vec2 point )
{
if ( point.x > 0.0 )
return ( point.y > 0.0) ? radii.x : radii.y;
else
return ( point.y > 0.0) ? radii.z : radii.w;
}
bool isInside( in lowp vec2 point, in lowp vec2 size, in lowp vec4 radii )
{
lowp float r = effectiveRadius( radii, point );
lowp vec2 d = abs(point) - size + r;
lowp float l = min( max(d.x, d.y), 0.0) + length( max(d, 0.0) ) - r;
return l <= 0.0;
}
lowp float shadowAt(
in lowp vec2 point, in lowp vec2 size, in lowp vec4 radii )
{
lowp float r = effectiveRadius( radii, point );
lowp vec2 d = abs(point) - size + r;
return min( max(d.x, d.y), 0.0) + length( max(d, 0.0) ) - r;
}
void main()
{
lowp vec4 col = vec4(0.0);
if ( extent > 0.0 && opacity > 0.0 )
{
lowp float t = 1.0 + 2.0 * length( offset ) + extent;
if ( !isInside( coord, aspect / t, radius / t) )
{
const lowp float minRadius = 0.05;
lowp float e2 = 0.5 * extent;
lowp vec4 f = minRadius / max( radius, minRadius );
lowp vec4 r = radius + e2 * f;
lowp float shadow = shadowAt(
coord - 2.0 * offset / t, aspect / t, r / t);
lowp float v = smoothstep( -e2, e2, shadow );
col = mix( color, vec4(0.0), v ) * opacity;
}
}
gl_FragColor = col;
}

View File

@ -0,0 +1,13 @@
uniform highp mat4 matrix;
uniform lowp vec2 aspect;
attribute highp vec4 in_vertex;
attribute mediump vec2 in_coord;
varying mediump vec2 coord;
void main()
{
coord = ( -1.0 + 2.0 * in_coord ) * aspect;
gl_Position = matrix * in_vertex;
}

View File

@ -0,0 +1,59 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#include "ShadowedBox.h"
#include <QGuiApplication>
#include <QskObjectCounter.h>
#include <QskWindow.h>
#include <QskLinearBox.h>
#include <QskBoxShapeMetrics.h>
#include <QskRgbValue.h>
class Box : public ShadowedBox
{
public:
Box( QQuickItem* parent = nullptr )
: ShadowedBox( parent )
{
Shadow shadow;
shadow.extent = 10;
shadow.xOffset = 20.0;
shadow.yOffset = 20.0;
setShadow( shadow );
setShadowColor( Qt::black );
setGradient( Qt::darkRed );
setShape( QskBoxShapeMetrics( 5, 10, 15, 20 ) );
}
};
int main( int argc, char* argv[] )
{
#ifdef ITEM_STATISTICS
QskObjectCounter counter( true );
#endif
QGuiApplication app( argc, argv );
auto layout = new QskLinearBox();
layout->setPanel( true );
#if 1
layout->setGradientHint( QskBox::Panel,
QskGradient( Qt::Vertical, QskRgb::WhiteSmoke, QskRgb::MistyRose ) );
#else
layout->setGradientHint( QskBox::Panel, Qt::white );
#endif
layout->setPadding( 60 );
(void ) new Box( layout );
QskWindow window;
window.setColor( QskRgb::PapayaWhip );
window.addItem( layout );
window.resize( 600, 600 );
window.show();
return app.exec();
}

View File

@ -0,0 +1,7 @@
<!DOCTYPE RCC>
<RCC version="1.0">
<qresource prefix="/qskinny/shaders">
<file>boxshadow.vert</file>
<file>boxshadow.frag</file>
</qresource>
</RCC>

View File

@ -0,0 +1,14 @@
CONFIG += qskexample
QT += quick_private
RESOURCES += \
shaders.qrc
HEADERS += \
ShadowedBox.h \
BoxShadowNode.h
SOURCES += \
ShadowedBox.cpp \
BoxShadowNode.cpp \
main.cpp