diff --git a/playground/playground.pro b/playground/playground.pro index 3fc1d24b..e1fef989 100644 --- a/playground/playground.pro +++ b/playground/playground.pro @@ -5,6 +5,7 @@ SUBDIRS += \ dialogbuttons \ invoker \ inputpanel \ + shadows \ images qtHaveModule(webengine) { diff --git a/playground/shadows/BoxShadowNode.cpp b/playground/shadows/BoxShadowNode.cpp index 4fb8adfe..50ebaf5d 100644 --- a/playground/shadows/BoxShadowNode.cpp +++ b/playground/shadows/BoxShadowNode.cpp @@ -27,13 +27,12 @@ namespace 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; + int m_matrixId = -1; + int m_opacityId = -1; + int m_aspectId = -1; + int m_blurExtentId = -1; + int m_radiusId = -1; + int m_colorId = -1; }; class Material final : public QSGMaterial @@ -48,10 +47,9 @@ namespace int compare( const QSGMaterial* other ) const override; QVector2D aspect = QVector2D{1.0, 1.0}; - float extent = 0.0; + float blurExtent = 0.0; QVector4D radius = QVector4D{0.0, 0.0, 0.0, 0.0}; QColor color = Qt::black; - QVector2D offset; }; Shader::Shader() @@ -74,13 +72,12 @@ namespace 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" ); + m_matrixId = p->uniformLocation( "matrix" ); + m_aspectId = p->uniformLocation( "aspect" ); + m_opacityId = p->uniformLocation( "opacity" ); + m_blurExtentId = p->uniformLocation( "blurExtent" ); + m_radiusId = p->uniformLocation( "radius" ); + m_colorId = p->uniformLocation( "color" ); } void Shader::updateState( const QSGMaterialShader::RenderState& state, @@ -89,20 +86,20 @@ namespace auto p = program(); if ( state.isMatrixDirty() ) - p->setUniformValue(m_matrixLocation, state.combinedMatrix() ); + p->setUniformValue( m_matrixId, state.combinedMatrix() ); if ( state.isOpacityDirty() ) - p->setUniformValue(m_opacityLocation, state.opacity() ); + p->setUniformValue( m_opacityId, state.opacity() ); - if ( oldMaterial == nullptr || newMaterial->compare( oldMaterial ) != 0 ) + 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 ); + p->setUniformValue( m_aspectId, material->aspect ); + p->setUniformValue( m_blurExtentId, material->blurExtent); + p->setUniformValue( m_radiusId, material->radius ); + p->setUniformValue( m_colorId, material->color ); } } @@ -127,9 +124,8 @@ namespace auto material = static_cast< const Material* >( other ); if ( material->color == color - && material->offset == offset && material->aspect == aspect - && qFuzzyCompare(material->extent, extent) + && qFuzzyCompare(material->blurExtent, blurExtent) && qFuzzyCompare(material->radius, radius) ) { return 0; @@ -175,16 +171,16 @@ void BoxShadowNode::setRect( const QRectF& rect ) d->rect = rect; - QVector2D newAspect( 1.0, 1.0 ); + QVector2D aspect( 1.0, 1.0 ); if ( rect.width() >= rect.height() ) - newAspect.setX( rect.width() / rect.height() ); + aspect.setX( rect.width() / rect.height() ); else - newAspect.setY( rect.height() / rect.width() ); + aspect.setY( rect.height() / rect.width() ); - if ( d->material.aspect != newAspect) + if ( d->material.aspect != aspect ) { - d->material.aspect = newAspect; + d->material.aspect = aspect; markDirty( QSGNode::DirtyMaterial ); } } @@ -193,7 +189,7 @@ void BoxShadowNode::setShape( const QskBoxShapeMetrics& shape ) { Q_D( BoxShadowNode ); - const float t = 0.5 * std::min( d->rect.width(), d->rect.height() ); + const float t = std::min( d->rect.width(), d->rect.height() ); const float r1 = shape.radius( Qt::BottomRightCorner ).width(); const float r2 = shape.radius( Qt::TopRightCorner ).width(); @@ -224,55 +220,38 @@ void BoxShadowNode::setColor( const QColor& color ) if ( d->material.color != c ) { d->material.color = c; - markDirty(QSGNode::DirtyMaterial); + markDirty( QSGNode::DirtyMaterial ); } } -void BoxShadowNode::setShadow( qreal extent, qreal dx, qreal dy ) +void BoxShadowNode::setBlurRadius( qreal blurRadius ) { Q_D( BoxShadowNode ); - if ( extent <= 0.0 ) - extent = 0.0; + if ( blurRadius <= 0.0 ) + blurRadius = 0.0; - const auto minDimension = std::min( d->rect.width(), d->rect.height() ); + const float t = 0.5 * std::min( d->rect.width(), d->rect.height() ); + const float uniformExtent = blurRadius / t; - const float uniformExtent = ( extent / minDimension ) * 2.0; - - if ( !qFuzzyCompare( d->material.extent, uniformExtent ) ) + if ( !qFuzzyCompare( d->material.blurExtent, uniformExtent ) ) { - d->material.extent = uniformExtent; - markDirty(QSGNode::DirtyMaterial); - } - - const auto uniformOffset = QVector2D( dx, dy ) / minDimension; - - if ( d->material.offset != uniformOffset) - { - d->material.offset = uniformOffset; + d->material.blurExtent = uniformExtent; markDirty( QSGNode::DirtyMaterial ); } } +void BoxShadowNode::setClipRect( const QRectF& rect ) +{ + Q_UNUSED( rect ) +} + 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 ) ); + &d->geometry, d->rect, QRectF( -0.5, -0.5, 1.0, 1.0 ) ); markDirty( QSGNode::DirtyGeometry ); } diff --git a/playground/shadows/BoxShadowNode.h b/playground/shadows/BoxShadowNode.h index e6dc46d4..bd84a5eb 100644 --- a/playground/shadows/BoxShadowNode.h +++ b/playground/shadows/BoxShadowNode.h @@ -21,7 +21,9 @@ class BoxShadowNode : public QSGGeometryNode void setRect( const QRectF& ); void setShape( const QskBoxShapeMetrics& ); void setColor( const QColor& ); - void setShadow( qreal extent, qreal dx, qreal dy ); + void setBlurRadius( qreal ); + + void setClipRect( const QRectF& ); void updateGeometry(); diff --git a/playground/shadows/ShadowedBox.cpp b/playground/shadows/ShadowedBox.cpp index f6246e3f..e3aa75e2 100644 --- a/playground/shadows/ShadowedBox.cpp +++ b/playground/shadows/ShadowedBox.cpp @@ -40,6 +40,10 @@ namespace { const auto box = static_cast< const ShadowedBox* >( skinnable ); + const auto r = box->subControlRect( ShadowedBox::Panel ); + if ( r.isEmpty() ) + return nullptr; + switch ( nodeRole ) { case ShadowRole: @@ -48,17 +52,14 @@ namespace 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; + const auto& shadowMetrics = box->shadow(); - auto r = box->subControlRect( ShadowedBox::Panel ); - r.adjust( -dx, -dy, dx, dy ); - - shadowNode->setRect( r ); + shadowNode->setRect( shadowMetrics.shadowRect( r ) ); shadowNode->setShape( box->shape() ); - shadowNode->setShadow( s.extent, s.xOffset, s.yOffset ); + shadowNode->setBlurRadius( shadowMetrics.blurRadius() ); shadowNode->setColor( box->shadowColor() ); + shadowNode->setClipRect( r ); + shadowNode->updateGeometry(); return shadowNode; @@ -69,10 +70,10 @@ namespace if ( boxNode == nullptr ) boxNode = new QskBoxNode(); - const QRectF r = box->subControlRect( ShadowedBox::Panel ); + const auto r = box->subControlRect( ShadowedBox::Panel ); - boxNode->setBoxData( r, box->shape(), QskBoxBorderMetrics(), - QskBoxBorderColors(), box->gradient() ); + boxNode->setBoxData( r, box->shape(), box->borderWidth(), + box->borderColor(), box->gradient() ); return boxNode; } @@ -96,13 +97,13 @@ ShadowedBox::~ShadowedBox() { } -void ShadowedBox::setShadow( const Shadow& shadow ) +void ShadowedBox::setShadow( const QskShadowMetrics& shadow ) { m_shadow = shadow; update(); } -const ShadowedBox::Shadow& ShadowedBox::shadow() const +const QskShadowMetrics& ShadowedBox::shadow() const { return m_shadow; } @@ -140,4 +141,26 @@ const QskBoxShapeMetrics& ShadowedBox::shape() const return m_shape; } +void ShadowedBox::setBorderWidth( qreal width ) +{ + m_borderWidth = qMax( width, 0.0 ); + update(); +} + +qreal ShadowedBox::borderWidth() const +{ + return m_borderWidth; +} + +void ShadowedBox::setBorderColor( const QColor& color ) +{ + m_borderColor = color; + update(); +} + +QColor ShadowedBox::borderColor() const +{ + return m_borderColor; +} + #include "moc_ShadowedBox.cpp" diff --git a/playground/shadows/ShadowedBox.h b/playground/shadows/ShadowedBox.h index 0682aefb..4f8b8dab 100644 --- a/playground/shadows/ShadowedBox.h +++ b/playground/shadows/ShadowedBox.h @@ -7,6 +7,7 @@ #include #include +#include class QskGradient; @@ -17,19 +18,11 @@ class ShadowedBox : public QskControl 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 setShadow( const QskShadowMetrics& ); + const QskShadowMetrics& shadow() const; void setGradient( const QskGradient& ); const QskGradient& gradient() const; @@ -40,9 +33,19 @@ class ShadowedBox : public QskControl void setShape( const QskBoxShapeMetrics& ); const QskBoxShapeMetrics& shape() const; + void setBorderWidth( qreal width ); + qreal borderWidth() const; + + void setBorderColor( const QColor& ); + QColor borderColor() const; + private: - Shadow m_shadow; + QskShadowMetrics m_shadow; QColor m_shadowColor = Qt::black; + QskGradient m_gradient; QskBoxShapeMetrics m_shape; + + qreal m_borderWidth = 0.0; + QColor m_borderColor = Qt::black; }; diff --git a/playground/shadows/boxshadow.frag b/playground/shadows/boxshadow.frag index 5224bed0..b24ff185 100644 --- a/playground/shadows/boxshadow.frag +++ b/playground/shadows/boxshadow.frag @@ -1,72 +1,42 @@ -/* - Heavily inspired by code from the kirigami project: - Copyright 2020 Arjen Hiemstra - 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 float blurExtent; 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; + if ( point.x > 0.0 ) + return ( point.y > 0.0) ? radii.x : radii.y; + else + return ( point.y > 0.0) ? radii.z : radii.w; } void main() { - lowp vec4 col = vec4(0.0); + lowp vec4 col = vec4(0.0); - if ( extent > 0.0 && opacity > 0.0 ) - { - lowp float t = 1.0 + 2.0 * length( offset ) + extent; + if ( opacity > 0.0 ) + { + const lowp float minRadius = 0.05; - if ( !isInside( coord, aspect / t, radius / t) ) - { - const lowp float minRadius = 0.05; - lowp float e2 = 0.5 * extent; + lowp float e2 = 0.5 * blurExtent; + lowp float r = 2.0 * effectiveRadius( radius, coord ); - lowp vec4 f = minRadius / max( radius, minRadius ); - lowp vec4 r = radius + e2 * f; + lowp float f = minRadius / max( r, minRadius ); - lowp float shadow = shadowAt( - coord - 2.0 * offset / t, aspect / t, r / t); + r += e2 * f; - lowp float v = smoothstep( -e2, e2, shadow ); + lowp vec2 d = r + blurExtent - aspect * ( 1.0 - abs( 2.0 * coord ) ); + lowp float l = min( max(d.x, d.y), 0.0) + length( max(d, 0.0) ); - col = mix( color, vec4(0.0), v ) * opacity; - } - } + lowp float shadow = l - r; + + lowp float v = smoothstep( -e2, e2, shadow ); + col = mix( color, vec4(0.0), v ) * opacity; + } gl_FragColor = col; } diff --git a/playground/shadows/boxshadow.vert b/playground/shadows/boxshadow.vert index 96d89780..5a55b751 100644 --- a/playground/shadows/boxshadow.vert +++ b/playground/shadows/boxshadow.vert @@ -8,6 +8,6 @@ varying mediump vec2 coord; void main() { - coord = ( -1.0 + 2.0 * in_coord ) * aspect; + coord = in_coord; gl_Position = matrix * in_vertex; } diff --git a/playground/shadows/main.cpp b/playground/shadows/main.cpp index 0e9b9821..90faf9cd 100644 --- a/playground/shadows/main.cpp +++ b/playground/shadows/main.cpp @@ -5,6 +5,8 @@ #include "ShadowedBox.h" +#include + #include #include #include @@ -18,15 +20,26 @@ class Box : public ShadowedBox Box( QQuickItem* parent = nullptr ) : ShadowedBox( parent ) { - Shadow shadow; - shadow.extent = 10; - shadow.xOffset = 20.0; - shadow.yOffset = 20.0; + const qreal w = 10; + + QskShadowMetrics shadow; + //shadow.setOffset( 20.0, 20.0 ); + shadow.setSpreadRadius( w ); + shadow.setBlurRadius( w ); setShadow( shadow ); setShadowColor( Qt::black ); - setGradient( Qt::darkRed ); - setShape( QskBoxShapeMetrics( 5, 10, 15, 20 ) ); + + QColor c( Qt::darkRed ); +#if 0 + c.setAlpha( 100 ); +#endif + + setGradient( c ); + setShape( QskBoxShapeMetrics( 40, 10, 15, 5 ) ); + + setBorderWidth( w ); + setBorderColor( Qt::blue ); } }; @@ -38,6 +51,8 @@ int main( int argc, char* argv[] ) QGuiApplication app( argc, argv ); + SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); + auto layout = new QskLinearBox(); layout->setPanel( true ); #if 1 diff --git a/src/common/QskShadowMetrics.cpp b/src/common/QskShadowMetrics.cpp index 560aae6b..5b275bc8 100644 --- a/src/common/QskShadowMetrics.cpp +++ b/src/common/QskShadowMetrics.cpp @@ -73,11 +73,14 @@ QVariant QskShadowMetrics::interpolate( QRectF QskShadowMetrics::shadowRect( const QRectF& sourceRect ) const { + const auto metrics = toAbsolute( sourceRect.size() ); + const auto extent = metrics.m_spreadRadius + metrics.m_blurRadius; + return QRectF( - sourceRect.x() + m_offset.x() - m_spreadRadius, - sourceRect.y() + m_offset.y() - m_spreadRadius, - sourceRect.width() + 2 * m_spreadRadius, - sourceRect.height() + 2 * m_spreadRadius ); + sourceRect.x() + metrics.m_offset.x() - extent, + sourceRect.y() + metrics.m_offset.y() - extent, + sourceRect.width() + 2 * extent, + sourceRect.height() + 2 * extent ); } uint QskShadowMetrics::hash( uint seed ) const noexcept