diff --git a/playground/shadows/ArcPage.cpp b/playground/shadows/ArcPage.cpp new file mode 100644 index 00000000..7ee25532 --- /dev/null +++ b/playground/shadows/ArcPage.cpp @@ -0,0 +1,109 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "ArcPage.h" +#include "ShadowedArc.h" +#include "Slider.h" + +#include + +namespace +{ + class ControlPanel : public QskGridBox + { + Q_OBJECT + + public: + ControlPanel( ShadowedArc* arc, QQuickItem* parent = nullptr ) + : QskGridBox( parent ) + { + setMargins( 5 ); + setSpacing( 10 ); + + { + auto slider = new Slider( "Start", 0, 360, 10, arc->startAngle() ); + connect( slider, &Slider::valueChanged, arc, &ShadowedArc::setStartAngle ); + + addItem( slider, 0, 0 ); + } + { + auto slider = new Slider( "Span", -360, 360, 10, arc->spanAngle() ); + connect( slider, &Slider::valueChanged, arc, &ShadowedArc::setSpanAngle ); + + addItem( slider, 0, 1 ); + } + { + auto slider = new Slider( "Extent", 0, 100, 1, arc->thickness() ); + connect( slider, &Slider::valueChanged, arc, &ShadowedArc::setThickness ); + + addItem( slider, 1, 0 ); + } + { + auto slider = new Slider( "Border", 0, 10, 1, arc->borderWidth() ); + connect( slider, &Slider::valueChanged, arc, &ShadowedArc::setBorderWidth ); + + addItem( slider, 1, 1); + } + { + auto slider = new Slider( "Spread Radius", -10, 50, 1, arc->spreadRadius() ); + connect( slider, &Slider::valueChanged, arc, &ShadowedArc::setSpreadRadius ); + + addItem( slider, 2, 0 ); + } + { + auto slider = new Slider( "Blur Radius", 0, 50, 1, arc->blurRadius() ); + connect( slider, &Slider::valueChanged, arc, &ShadowedArc::setBlurRadius ); + + addItem( slider, 2, 1 ); + } + { + auto slider = new Slider( "Offset X", -50, 50, 1, arc->offsetX() ); + connect( slider, &Slider::valueChanged, arc, &ShadowedArc::setOffsetX ); + + addItem( slider, 3, 0 ); + + } + { + auto slider = new Slider( "Offset Y", -50, 50, 1, arc->offsetY() ); + connect( slider, &Slider::valueChanged, arc, &ShadowedArc::setOffsetY ); + + addItem( slider, 3, 1 ); + } + } + }; +} + +ArcPage::ArcPage( QQuickItem* parent ) + : QskLinearBox( Qt::Vertical, parent ) +{ + auto arc = new ShadowedArc(); + arc->setMargins( 40 ); // some extra space for testing the offsets + + { + // initial settings + arc->setStartAngle( 45.0 ); + arc->setSpanAngle( 270.0 ); + arc->setThickness( 10.0 ); + + arc->setFillColor( Qt::darkRed ); + + arc->setBorderWidth( 0 ); + arc->setBorderColor( Qt::darkYellow ); + + arc->setShadowColor( Qt::black ); + arc->setSpreadRadius( 0.0 ); + arc->setBlurRadius( 4.0 ); + arc->setOffsetX( 2.0 ); + arc->setOffsetY( 2.0 ); + } + + auto panel = new ControlPanel( arc ); + panel->setSizePolicy( Qt::Vertical, QskSizePolicy::Fixed ); + + addItem( panel ); + addItem( arc ); +} + +#include "ArcPage.moc" diff --git a/playground/shadows/ArcPage.h b/playground/shadows/ArcPage.h new file mode 100644 index 00000000..eeb62066 --- /dev/null +++ b/playground/shadows/ArcPage.h @@ -0,0 +1,14 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#pragma once + +#include + +class ArcPage : public QskLinearBox +{ + public: + ArcPage( QQuickItem* parent = nullptr ); +}; diff --git a/playground/shadows/BoxPage.cpp b/playground/shadows/BoxPage.cpp new file mode 100644 index 00000000..860c2c0f --- /dev/null +++ b/playground/shadows/BoxPage.cpp @@ -0,0 +1,73 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "BoxPage.h" +#include "ShadowedBox.h" +#include "Slider.h" + +#include +#include + +namespace +{ + class ControlPanel : public QskLinearBox + { + public: + ControlPanel( ShadowedBox* box, QQuickItem* parent = nullptr ) + : QskLinearBox( Qt::Vertical, parent ) + { + { + auto slider = new Slider( "Offset X", -50, 50, 1, box->offsetX() ); + connect( slider, &Slider::valueChanged, box, &ShadowedBox::setOffsetX ); + + addItem( slider ); + } + { + auto slider = new Slider( "Offset Y", -50, 50, 1, box->offsetY() ); + connect( slider, &Slider::valueChanged, box, &ShadowedBox::setOffsetY ); + + addItem( slider ); + } + { + auto slider = new Slider( "Spread Radius", -10, 50, 1, box->spreadRadius() ); + connect( slider, &Slider::valueChanged, box, &ShadowedBox::setSpreadRadius ); + + addItem( slider ); + } + { + auto slider = new Slider( "Blur Radius", 0, 50, 1, box->blurRadius() ); + connect( slider, &Slider::valueChanged, box, &ShadowedBox::setBlurRadius ); + + addItem( slider ); + } + { + auto slider = new Slider( "Opacity", 0, 1, 0.01, box->opacity() ); + connect( slider, &Slider::valueChanged, box, &ShadowedBox::setOpacity ); + + addItem( slider ); + } + } + }; +} + +BoxPage::BoxPage( QQuickItem* parent ) + : QskLinearBox( Qt::Vertical, parent ) +{ + auto box = new ShadowedBox(); + box->setMargins( 40 ); // some extra space for testing the offsets + + { + box->setOffsetX( 10 ); + box->setOffsetY( 10 ); + box->setSpreadRadius( 0 ); + box->setBlurRadius( 5 ); + } + + auto panel = new ControlPanel( box ); + panel->setSizePolicy( Qt::Vertical, QskSizePolicy::Fixed ); + + addItem( panel ); + addItem( box ); +} diff --git a/playground/shadows/BoxPage.h b/playground/shadows/BoxPage.h new file mode 100644 index 00000000..d5dc15aa --- /dev/null +++ b/playground/shadows/BoxPage.h @@ -0,0 +1,19 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#pragma once + +#include + +class QskSlider; + +class BoxPage : public QskLinearBox +{ + public: + BoxPage( QQuickItem* parent = nullptr ); + + private: + void addSlider( int row, const QString&, QskSlider* ); +}; diff --git a/playground/shadows/CMakeLists.txt b/playground/shadows/CMakeLists.txt index 0d3466d7..f65ac316 100644 --- a/playground/shadows/CMakeLists.txt +++ b/playground/shadows/CMakeLists.txt @@ -3,4 +3,7 @@ # SPDX-License-Identifier: BSD-3-Clause ############################################################################ -qsk_add_example(shadows ShadowedBox.h ShadowedBox.cpp main.cpp) +qsk_add_example(shadows + BoxPage.h BoxPage.cpp ShadowedBox.h ShadowedBox.cpp + ArcPage.h ArcPage.cpp ShadowedArc.h ShadowedArc.cpp + Slider.h Slider.cpp main.cpp) diff --git a/playground/shadows/ShadowedArc.cpp b/playground/shadows/ShadowedArc.cpp new file mode 100644 index 00000000..0ba49ad5 --- /dev/null +++ b/playground/shadows/ShadowedArc.cpp @@ -0,0 +1,269 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "ShadowedArc.h" + +#include +#include +#include +#include + +#include +#include + +QSK_SUBCONTROL( ShadowedArc, Arc ) + +namespace +{ + class Skinlet : public QskSkinlet + { + using Inherited = QskSkinlet; + + public: + enum NodeRoles { ArcRole }; + + Skinlet( QskSkin* skin = nullptr ); + + QRectF subControlRect( const QskSkinnable*, + const QRectF&, QskAspect::Subcontrol ) const override; + + QSGNode* updateSubNode( const QskSkinnable*, + quint8 nodeRole, QSGNode* ) const override; + + private: + QSGNode* updateArcNode( const ShadowedArc*, QSGNode* node ) const; + }; + + Skinlet::Skinlet( QskSkin* skin ) + : QskSkinlet( skin ) + { + setNodeRoles( { ArcRole } ); + } + + QRectF Skinlet::subControlRect( const QskSkinnable* skinnable, + const QRectF& contentsRect, QskAspect::Subcontrol subControl ) const + { + if ( subControl == ShadowedArc::Arc ) + return contentsRect; + + return Inherited::subControlRect( skinnable, contentsRect, subControl ); + } + + QSGNode* Skinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const + { + if ( nodeRole == ArcRole ) + { + auto arc = static_cast< const ShadowedArc* >( skinnable ); + return updateArcNode( arc, node ); + } + + return Inherited::updateSubNode( skinnable, nodeRole, node ); + } + + QSGNode* Skinlet::updateArcNode( const ShadowedArc* arc, QSGNode* node ) const + { + using Q = ShadowedArc; + + const auto rect = arc->subControlRect( Q::Arc ); + if ( rect.isEmpty() ) + return nullptr; + + auto arcNode = QskSGNode::ensureNode< QskArcNode >( node ); + + const auto metrics = arc->arcMetricsHint( Q::Arc ); + const auto fillGradient = arc->gradientHint( Q::Arc ); + + const auto borderColor = arc->color( Q::Arc | QskAspect::Border ); + const auto borderWidth = arc->metric( Q::Arc | QskAspect::Border ); + + const auto shadowColor = arc->shadowColorHint( Q::Arc ); + const auto shadowMetrics = arc->shadowMetricsHint( Q::Arc ); + + arcNode->setArcData( rect, metrics, borderWidth, borderColor, + fillGradient, shadowColor, shadowMetrics); + + return arcNode; + } +} + +ShadowedArc::ShadowedArc( QQuickItem* parent ) + : Inherited( parent ) +{ + auto skinlet = new Skinlet(); + skinlet->setOwnedBySkinnable( true ); + + setSkinlet( skinlet ); + + // initial settings + + setArcMetrics( { 0.0, 360.0, 1.0, Qt::RelativeSize } ); + + setFillColor( Qt::darkRed ); + + setBorderWidth( 0 ); + setBorderColor( Qt::gray ); + + setShadowColor( Qt::black ); + setShadowMetrics( { 0, 0, QPointF( 0, 0 ), Qt::AbsoluteSize } ); +} + +ShadowedArc::~ShadowedArc() +{ +} + +void ShadowedArc::setThickness( qreal thickness ) +{ + auto metrics = arcMetrics(); + metrics.setThickness( thickness ); + + setArcMetrics( metrics ); +} + +qreal ShadowedArc::thickness() const +{ + return arcMetrics().thickness(); +} + +void ShadowedArc::setBorderWidth( qreal width ) +{ + width = std::max( width, 0.0 ); + setMetric( Arc | QskAspect::Border, width ); +} + +qreal ShadowedArc::borderWidth() const +{ + return metric( Arc | QskAspect::Border ); +} + +void ShadowedArc::setStartAngle( qreal degrees ) +{ + auto metrics = arcMetrics(); + metrics.setStartAngle( degrees ); + + setArcMetrics( metrics ); +} + +qreal ShadowedArc::startAngle() const +{ + return arcMetrics().startAngle(); +} + +void ShadowedArc::setSpanAngle( qreal degrees ) +{ + auto metrics = arcMetrics(); + metrics.setSpanAngle( degrees ); + + setArcMetrics( metrics ); +} + +qreal ShadowedArc::spanAngle() const +{ + return arcMetrics().spanAngle(); +} + +void ShadowedArc::setOffsetX( qreal dx ) +{ + auto metrics = shadowMetrics(); + metrics.setOffsetX( dx ); + + setShadowMetrics( metrics ); +} + +qreal ShadowedArc::offsetX() const +{ + return shadowMetrics().offset().x(); +} + +void ShadowedArc::setOffsetY( qreal dy ) +{ + auto metrics = shadowMetrics(); + metrics.setOffsetY( dy ); + + setShadowMetrics( metrics ); +} + +qreal ShadowedArc::offsetY() const +{ + return shadowMetrics().offset().y(); +} + +void ShadowedArc::setSpreadRadius( qreal radius ) +{ + auto metrics = shadowMetrics(); + metrics.setSpreadRadius( radius ); + + setShadowMetrics( metrics ); +} + +qreal ShadowedArc::spreadRadius() const +{ + return shadowMetrics().spreadRadius(); +} + +void ShadowedArc::setBlurRadius( qreal radius ) +{ + auto metrics = shadowMetrics(); + metrics.setBlurRadius( radius ); + + setShadowMetrics( metrics ); +} + +qreal ShadowedArc::blurRadius() const +{ + return shadowMetrics().blurRadius(); +} + +void ShadowedArc::setFillColor( const QColor& color ) +{ + setColor( Arc, color ); +} + +QColor ShadowedArc::fillColor() const +{ + return color( Arc ); +} + +void ShadowedArc::setShadowColor( const QColor& color ) +{ + setShadowColorHint( Arc, color ); +} + +QColor ShadowedArc::shadowColor() const +{ + return shadowColorHint( Arc ); +} + +void ShadowedArc::setBorderColor( const QColor& color ) +{ + setColor( Arc | QskAspect::Border, color ); +} + +QColor ShadowedArc::borderColor() const +{ + return color( Arc | QskAspect::Border ); +} + +QskShadowMetrics ShadowedArc::shadowMetrics() const +{ + return shadowMetricsHint( Arc ); +} + +void ShadowedArc::setShadowMetrics( const QskShadowMetrics& metrics ) +{ + setShadowMetricsHint( Arc, metrics ); +} + +QskArcMetrics ShadowedArc::arcMetrics() const +{ + return arcMetricsHint( Arc ); +} + +void ShadowedArc::setArcMetrics( const QskArcMetrics& metrics ) +{ + setArcMetricsHint( Arc, metrics ); +} + +#include "moc_ShadowedArc.cpp" diff --git a/playground/shadows/ShadowedArc.h b/playground/shadows/ShadowedArc.h new file mode 100644 index 00000000..a4179ade --- /dev/null +++ b/playground/shadows/ShadowedArc.h @@ -0,0 +1,64 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#pragma once + +#include + +class QskShadowMetrics; +class QskArcMetrics; + +class ShadowedArc : public QskControl +{ + Q_OBJECT + + using Inherited = QskControl; + + public: + QSK_SUBCONTROLS( Arc ) + + ShadowedArc( QQuickItem* parent = nullptr ); + ~ShadowedArc() override; + + qreal thickness() const; + qreal borderWidth() const; + + qreal startAngle() const; + qreal spanAngle() const; + + qreal offsetX() const; + qreal offsetY() const; + + qreal spreadRadius() const; + qreal blurRadius() const; + + QColor borderColor() const; + QColor fillColor() const; + QColor shadowColor() const; + + public Q_SLOTS: + void setThickness( qreal ); + void setBorderWidth( qreal ); + + void setStartAngle( qreal ); + void setSpanAngle( qreal ); + + void setOffsetX( qreal ); + void setOffsetY( qreal ); + + void setSpreadRadius( qreal ); + void setBlurRadius( qreal ); + + void setBorderColor( const QColor& ); + void setFillColor( const QColor& ); + void setShadowColor( const QColor& ); + + private: + QskShadowMetrics shadowMetrics() const; + void setShadowMetrics( const QskShadowMetrics& ); + + QskArcMetrics arcMetrics() const; + void setArcMetrics( const QskArcMetrics& ); +}; diff --git a/playground/shadows/ShadowedBox.cpp b/playground/shadows/ShadowedBox.cpp index 2431e80e..cad0fd22 100644 --- a/playground/shadows/ShadowedBox.cpp +++ b/playground/shadows/ShadowedBox.cpp @@ -14,21 +14,10 @@ ShadowedBox::ShadowedBox( QQuickItem* parentItem ) : QskBox( true, parentItem ) { - QColor c( Qt::darkRed ); -#if 0 - c.setAlpha( 100 ); -#endif - - setGradientHint( Panel, c ); + setGradientHint( Panel, Qt::darkRed ); setBoxShapeHint( Panel, QskBoxShapeMetrics( 40, 0, 15, 0 ) ); setBoxBorderMetricsHint( Panel, 0 ); - -#if 0 - setBoxBorderMetricsHint( Panel, 10 ); - setBoxBorderColorsHint( Panel, Qt::blue ); -#endif - setShadowColorHint( Panel, Qt::black ); } diff --git a/playground/shadows/Slider.cpp b/playground/shadows/Slider.cpp new file mode 100644 index 00000000..0a08201a --- /dev/null +++ b/playground/shadows/Slider.cpp @@ -0,0 +1,47 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "Slider.h" + +#include +#include + +#include + +Slider::Slider( const QString& text, qreal min, qreal max, + qreal step, qreal value, QQuickItem* parent ) + : QskLinearBox( Qt::Horizontal, parent ) +{ + m_label = new QskTextLabel( text, this ); + m_label->setSizePolicy( Qt::Horizontal, QskSizePolicy::Fixed ); + + m_slider = new QskSlider( this ); + m_slider->setBoundaries( min, max ); + m_slider->setStepSize( step ); + m_slider->setSnap( true ); + m_slider->setValue( value ); + + m_valueLabel = new QskTextLabel( this ); + m_valueLabel->setAlignment( Qt::AlignLeft | Qt::AlignVCenter ); + updateLabel( value ); + + const QFontMetricsF fm( m_valueLabel->font() ); + m_valueLabel->setFixedWidth( fm.horizontalAdvance( "-100" ) ); + + connect( m_slider, &QskSlider::valueChanged, this, &Slider::updateLabel ); + connect( m_slider, &QskSlider::valueChanged, this, &Slider::valueChanged ); +} + +void Slider::updateLabel( qreal value ) +{ + m_valueLabel->setText( QString::number( value ) ); +} + +void Slider::setValue( qreal value ) +{ + m_slider->setValue( value ); +} + +#include "moc_Slider.cpp" diff --git a/playground/shadows/Slider.h b/playground/shadows/Slider.h new file mode 100644 index 00000000..c62fa160 --- /dev/null +++ b/playground/shadows/Slider.h @@ -0,0 +1,37 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#pragma once + +#include + +class QskSlider; +class QskTextLabel; + +class Slider : public QskLinearBox +{ + Q_OBJECT + + using Inherited = QskLinearBox; + + public: + Slider( const QString&, qreal min, qreal max, qreal step, + qreal value, QQuickItem* parent = nullptr ); + + qreal value() const; + + Q_SIGNALS: + void valueChanged( qreal ); + + public Q_SLOTS: + void setValue( qreal ); + + private: + void updateLabel( qreal ); + + QskTextLabel* m_label = nullptr; + QskSlider* m_slider = nullptr; + QskTextLabel* m_valueLabel = nullptr; +}; diff --git a/playground/shadows/main.cpp b/playground/shadows/main.cpp index 2d6d6de9..c4e2ceda 100644 --- a/playground/shadows/main.cpp +++ b/playground/shadows/main.cpp @@ -3,129 +3,31 @@ * SPDX-License-Identifier: BSD-3-Clause *****************************************************************************/ -#include "ShadowedBox.h" +#include "BoxPage.h" +#include "ArcPage.h" #include #include -#include -#include -#include -#include +#include +#include #include #include -#include -class BoxPanel : public QskBox +namespace { - public: - BoxPanel( QQuickItem* parent = nullptr ) - : QskBox( parent ) + class TabView : public QskTabView { - setAutoLayoutChildren( true ); - setPadding( 60 ); + public: + TabView() + { + //setTabBarEdge( Qt::LeftEdge ); - setPanel( true ); - setGradientHint( QskBox::Panel, QGradient::SnowAgain ); - } -}; - -class Slider : public QskSlider -{ - public: - Slider( qreal min, qreal max, qreal step, qreal value, QQuickItem* parent = nullptr ) - : QskSlider( parent ) - { - setBoundaries( min, max ); - setStepSize( step ); - setSnap( true ); - setValue( value ); - } -}; - -class ValueLabel : public QskTextLabel -{ - public: - ValueLabel( QQuickItem* parent = nullptr ) - : QskTextLabel( parent ) - { - setFixedWidth( QFontMetrics( font() ).horizontalAdvance( "-100" ) ); - setAlignment( Qt::AlignLeft | Qt::AlignVCenter ); - } - - void setValue( qreal value ) - { - setText( QString::number( value ) ); - } -}; - -class GridBox : public QskGridBox -{ - public: - GridBox( QQuickItem* parent = nullptr ) - : QskGridBox( parent ) - { - setPanel( true ); - setPadding( 5 ); - setColumnStretchFactor( 1, 1 ); - - auto sliderX = new Slider( -50, 50, 1, 10 ); - auto sliderY = new Slider( -50, 50, 1, 10 ); - auto sliderSpread = new Slider( 0, 50, 1, 0 ); - auto sliderBlur = new Slider( 0, 50, 1, 10 ); - auto sliderOpacity = new Slider( 0, 1, 0.01, 1 ); - - auto panel = new BoxPanel(); - - int row = 0; - - addSlider( row++, "Offset X", sliderX ); - addSlider( row++, "Offset Y", sliderY ); - addSlider( row++, "Spread Radius", sliderSpread ); - addSlider( row++, "Blur Radius", sliderBlur ); - addSlider( row++, "Opacity", sliderOpacity ); - - addItem( panel, row, 0, -1, -1 ); - - auto box = new ShadowedBox( panel ); - - box->setOffsetX( sliderX->value() ); - box->setOffsetY( sliderY->value() ); - box->setSpreadRadius( sliderSpread->value() ); - box->setBlurRadius( sliderBlur->value() ); - box->setOpacity( sliderOpacity->value() ); - - connect( sliderX, &QskSlider::valueChanged, - box, &ShadowedBox::setOffsetX ); - - connect( sliderY, &QskSlider::valueChanged, - box, &ShadowedBox::setOffsetY ); - - connect( sliderSpread, &QskSlider::valueChanged, - box, &ShadowedBox::setSpreadRadius ); - - connect( sliderBlur, &QskSlider::valueChanged, - box, &ShadowedBox::setBlurRadius ); - - connect( sliderOpacity, &QskSlider::valueChanged, - box, &ShadowedBox::setOpacity ); - } - - private: - void addSlider( int row, const QString& text, QskSlider* slider ) - { - addItem( new QskTextLabel( text ), row, 0 ); - addItem( slider, row, 1 ); - - auto label = new ValueLabel(); - label->setValue( slider->value() ); - - addItem( label, row, 2 ); - - connect( slider, &QskSlider::valueChanged, - label, [label]( qreal value ) { label->setText( QString::number( value ) ); } ); - } -}; + addTab( "Arc Shadow", new ArcPage() ); + addTab( "Box Shadow", new BoxPage() ); + } + }; +} int main( int argc, char* argv[] ) { @@ -138,7 +40,7 @@ int main( int argc, char* argv[] ) SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts ); QskWindow window; - window.addItem( new GridBox() ); + window.addItem( new TabView() ); window.resize( 600, 600 ); window.show(); diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 3906ad94..3c6b4970 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -99,6 +99,7 @@ list(APPEND SOURCES list(APPEND HEADERS nodes/QskArcNode.h + nodes/QskArcShadowNode.h nodes/QskBasicLinesNode.h nodes/QskBoxNode.h nodes/QskBoxClipNode.h @@ -138,6 +139,7 @@ list(APPEND PRIVATE_HEADERS list(APPEND SOURCES nodes/QskArcNode.cpp + nodes/QskArcShadowNode.cpp nodes/QskBasicLinesNode.cpp nodes/QskBoxNode.cpp nodes/QskBoxClipNode.cpp diff --git a/src/controls/QskSkinlet.cpp b/src/controls/QskSkinlet.cpp index e6351519..1924339e 100644 --- a/src/controls/QskSkinlet.cpp +++ b/src/controls/QskSkinlet.cpp @@ -231,7 +231,7 @@ static inline QSGNode* qskUpdateArcNode( return nullptr; auto arcNode = QskSGNode::ensureNode< QskArcNode >( node ); - arcNode->setArcData( rect, metrics, borderWidth, borderColor, gradient ); + arcNode->setArcData( rect, metrics, borderWidth, borderColor, gradient, {}, {} ); return arcNode; } @@ -547,11 +547,11 @@ QSGNode* QskSkinlet::updateArcNode( const QskSkinnable* skinnable, } QSGNode* QskSkinlet::updateArcNode( - const QskSkinnable* skinnable, QSGNode* node, const QRectF& rect, + const QskSkinnable* skinnable, QSGNode* node, const QRectF& rect, qreal borderWidth, const QColor& borderColor, const QskGradient& fillGradient, const QskArcMetrics& metrics ) { - return qskUpdateArcNode( skinnable, node, rect, + return qskUpdateArcNode( skinnable, node, rect, borderWidth, borderColor, fillGradient, metrics ); } @@ -594,7 +594,7 @@ QSGNode* QskSkinlet::updateArcNode( const QskSkinnable* skinnable, } QSGNode* QskSkinlet::updateLineNode( const QskSkinnable* skinnable, - QSGNode* node, const QLineF& line, QskAspect::Subcontrol subControl ) + QSGNode* node, const QLineF& line, QskAspect::Subcontrol subControl ) { auto lineStipple = skinnable->stippleMetricsHint( subControl ); if ( !lineStipple.isValid() ) diff --git a/src/nodes/QskArcNode.cpp b/src/nodes/QskArcNode.cpp index a58a72df..ad280879 100644 --- a/src/nodes/QskArcNode.cpp +++ b/src/nodes/QskArcNode.cpp @@ -5,15 +5,27 @@ #include "QskArcNode.h" #include "QskArcMetrics.h" +#include "QskArcShadowNode.h" #include "QskMargins.h" #include "QskGradient.h" #include "QskShapeNode.h" #include "QskStrokeNode.h" #include "QskSGNode.h" +#include "QskShadowMetrics.h" #include #include +namespace +{ + enum NodeRole + { + ShadowRole, + FillRole, + BorderRole + }; +} + static inline QskGradient qskEffectiveGradient( const QskGradient& gradient, const QskArcMetrics& metrics ) { @@ -54,6 +66,14 @@ static inline QRectF qskEffectiveRect( return qskValidOrEmptyInnerRect( rect, QskMargins( 0.5 * borderWidth ) ); } +static void qskUpdateChildren( QSGNode* parentNode, quint8 role, QSGNode* node ) +{ + static const QVector< quint8 > roles = { ShadowRole, FillRole, BorderRole }; + + auto oldNode = QskSGNode::findChildNode( parentNode, role ); + QskSGNode::replaceChildNode( roles, role, parentNode, oldNode, node ); +} + QskArcNode::QskArcNode() { } @@ -65,20 +85,24 @@ QskArcNode::~QskArcNode() void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics, const QskGradient& fillGradient ) { - setArcData( rect, arcMetrics, 0.0, QColor(), fillGradient ); + setArcData( rect, arcMetrics, 0.0, QColor(), fillGradient, {}, {} ); } void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics, - qreal borderWidth, const QColor& borderColor, const QskGradient& fillGradient ) + const qreal borderWidth, const QColor& borderColor, const QskGradient& fillGradient ) { - enum NodeRole - { - FillRole, - BorderRole - }; + setArcData( rect, arcMetrics, borderWidth, borderColor, fillGradient, {}, {} ); +} - const auto metrics = qskEffectiveMetrics( arcMetrics, rect ); - const auto gradient = qskEffectiveGradient( fillGradient, metrics ); +void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics, + const qreal borderWidth, const QColor& borderColor, const QskGradient& fillGradient, + const QColor& shadowColor, const QskShadowMetrics& shadowMetrics ) +{ + const auto metricsArc = qskEffectiveMetrics( arcMetrics, rect ); + const auto gradient = qskEffectiveGradient( fillGradient, metricsArc ); + + auto shadowNode = static_cast< QskArcShadowNode* >( + QskSGNode::findChildNode( this, ShadowRole ) ); auto fillNode = static_cast< QskShapeNode* >( QskSGNode::findChildNode( this, FillRole ) ); @@ -89,22 +113,52 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics const auto arcRect = qskEffectiveRect( rect, borderWidth ); if ( arcRect.isEmpty() ) { + delete shadowNode; delete fillNode; delete borderNode; - return; } - const auto path = metrics.painterPath( arcRect ); + const auto isFillNodeVisible = gradient.isVisible() && !metricsArc.isNull(); + const auto isStrokeNodeVisible = borderWidth > 0.0 && borderColor.alpha() > 0; + const auto isShadowNodeVisible = shadowColor.alpha() > 0.0 && isFillNodeVisible; - if ( gradient.isVisible() && !metrics.isNull() ) + const auto path = metricsArc.painterPath( arcRect ); + + if ( isShadowNodeVisible ) + { + if ( shadowNode == nullptr ) + { + shadowNode = new QskArcShadowNode; + QskSGNode::setNodeRole( shadowNode, ShadowRole ); + } + + /* + The shader of the shadow node is for circular arcs and we have some + unwanted scaling issues for the spread/blur values when having ellipsoid + arcs. We might also want to add the spread value to the ends of the arc + and not only to its radius. TODO ... + */ + + const auto sm = shadowMetrics.toAbsolute( arcRect.size() ); + const auto shadowRect = sm.shadowRect( arcRect ); + const auto spreadRadius = sm.spreadRadius() + 0.5 * metricsArc.thickness(); + + shadowNode->setShadowData( shadowRect, spreadRadius, sm.blurRadius(), + metricsArc.startAngle(), metricsArc.spanAngle(), shadowColor ); + } + else + { + delete shadowNode; + shadowNode = nullptr; + } + + if ( isFillNodeVisible ) { if ( fillNode == nullptr ) { fillNode = new QskShapeNode; QskSGNode::setNodeRole( fillNode, FillRole ); - - prependChildNode( fillNode ); } fillNode->updateNode( path, QTransform(), arcRect, gradient ); @@ -112,25 +166,29 @@ void QskArcNode::setArcData( const QRectF& rect, const QskArcMetrics& arcMetrics else { delete fillNode; + fillNode = nullptr; } - if ( borderWidth > 0.0 && borderColor.alpha() > 0 ) + if ( isStrokeNodeVisible ) { if ( borderNode == nullptr ) { borderNode = new QskStrokeNode; QskSGNode::setNodeRole( borderNode, BorderRole ); - - appendChildNode( borderNode ); } QPen pen( borderColor, borderWidth ); pen.setCapStyle( Qt::FlatCap ); - + borderNode->updateNode( path, QTransform(), pen ); } else { delete borderNode; + borderNode = nullptr; } + + qskUpdateChildren(this, ShadowRole, shadowNode); + qskUpdateChildren(this, FillRole, fillNode); + qskUpdateChildren(this, BorderRole, borderNode); } diff --git a/src/nodes/QskArcNode.h b/src/nodes/QskArcNode.h index 1e3e44d1..16784f2a 100644 --- a/src/nodes/QskArcNode.h +++ b/src/nodes/QskArcNode.h @@ -10,6 +10,7 @@ class QskArcMetrics; class QskGradient; +class QskShadowMetrics; /* For the moment a QPainterPath/QskShapeNode. @@ -23,8 +24,13 @@ class QSK_EXPORT QskArcNode : public QskShapeNode ~QskArcNode() override; void setArcData( const QRectF&, const QskArcMetrics&, const QskGradient& ); + void setArcData( const QRectF&, const QskArcMetrics&, qreal borderWidth, const QColor& borderColor, const QskGradient& ); + + void setArcData( const QRectF&, const QskArcMetrics&, + qreal borderWidth, const QColor& borderColor, const QskGradient&, + const QColor& shadowColor, const QskShadowMetrics&); }; #endif diff --git a/src/nodes/QskArcShadowNode.cpp b/src/nodes/QskArcShadowNode.cpp new file mode 100644 index 00000000..8ced5062 --- /dev/null +++ b/src/nodes/QskArcShadowNode.cpp @@ -0,0 +1,368 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#include "QskArcShadowNode.h" + +#include +#include +#include +#include + +#include + +QSK_QT_PRIVATE_BEGIN +#include +QSK_QT_PRIVATE_END + +// QSGMaterialRhiShader became QSGMaterialShader in Qt6 + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) +#include +using RhiShader = QSGMaterialRhiShader; +#else +using RhiShader = QSGMaterialShader; +#endif + +namespace +{ + class Material final : public QSGMaterial + { + public: + Material(); + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + QSGMaterialShader* createShader() const override; +#else + QSGMaterialShader* createShader( QSGRendererInterface::RenderMode ) const override; +#endif + + QSGMaterialType* type() const override; + int compare( const QSGMaterial* other ) const override; + + QVector4D m_color { 0, 0, 0, 1 }; + QVector4D m_arc = {}; + + float m_spreadRadius = 0.0f; + float m_blurRadius = 0.0f; + }; +} + +namespace +{ + class ShaderRhi final : public RhiShader + { + public: + ShaderRhi() + { + const QString root( ":/qskinny/shaders/" ); + setShaderFileName( VertexStage, root + "arcshadow.vert.qsb" ); + setShaderFileName( FragmentStage, root + "arcshadow.frag.qsb" ); + } + + bool updateUniformData( RenderState& state, + QSGMaterial* const newMaterial, QSGMaterial* const oldMaterial ) override + { + const auto matOld = static_cast< Material* >( oldMaterial ); + const auto matNew = static_cast< Material* >( newMaterial ); + + Q_ASSERT( state.uniformData()->size() == 108 ); + + auto data = state.uniformData()->data(); + bool changed = false; + + if ( state.isMatrixDirty() ) + { + const auto matrix = state.combinedMatrix(); + memcpy( data + 0, matrix.constData(), 64 ); + + changed = true; + } + + if ( matOld == nullptr || matNew->m_color != matOld->m_color ) + { + memcpy( data + 64, &matNew->m_color, 16 ); + changed = true; + } + + if ( matOld == nullptr || matNew->m_arc != matOld->m_arc ) + { + memcpy( data + 80, &matNew->m_arc, 16 ); + changed = true; + } + + if ( matOld == nullptr || matNew->m_spreadRadius != matOld->m_spreadRadius ) + { + memcpy( data + 96, &matNew->m_spreadRadius, 4 ); + changed = true; + } + + if ( matOld == nullptr || matNew->m_blurRadius != matOld->m_blurRadius ) + { + memcpy( data + 100, &matNew->m_blurRadius, 4 ); + changed = true; + } + + if ( state.isOpacityDirty() ) + { + const float opacity = state.opacity(); + memcpy( data + 104, &opacity, 4 ); + + changed = true; + } + + return changed; + } + }; +} + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + +namespace +{ + // the old type of shader - specific for OpenGL + + class ShaderGL final : public QSGMaterialShader + { + struct Uniforms + { + int matrix = -1; + int color = -1; + int arc = -1; + int spreadRadius = -1; + int blurRadius = -1; + int opacity = -1; + }; + + public: + ShaderGL() + { + const QString root( ":/qskinny/shaders/" ); + setShaderSourceFile( QOpenGLShader::Vertex, root + "arcshadow.vert" ); + setShaderSourceFile( QOpenGLShader::Fragment, root + "arcshadow.frag" ); + } + + char const* const* attributeNames() const override + { + static char const* const names[] = { "in_vertex", "in_coord", nullptr }; + return names; + } + + void initialize() override + { + QSGMaterialShader::initialize(); + + const auto* const p = program(); + + id.matrix = p->uniformLocation( "matrix" ); + id.color = p->uniformLocation( "color" ); + id.arc = p->uniformLocation( "arc" ); + id.spreadRadius = p->uniformLocation( "spreadRadius" ); + id.blurRadius = p->uniformLocation( "blurRadius" ); + id.opacity = p->uniformLocation( "opacity" ); + } + + void updateState( const QSGMaterialShader::RenderState& state, + QSGMaterial* const newMaterial, QSGMaterial* const oldMaterial ) override + { + auto* const p = program(); + + if ( state.isMatrixDirty() ) + { + p->setUniformValue( id.matrix, state.combinedMatrix() ); + } + + if ( state.isOpacityDirty() ) + { + p->setUniformValue( id.opacity, state.opacity() ); + } + + auto updateMaterial = ( oldMaterial == nullptr ) || + ( newMaterial->compare( oldMaterial ) != 0 ); + + updateMaterial |= state.isCachedMaterialDataDirty(); + + if ( updateMaterial ) + { + const auto* const material = static_cast< const Material* >( newMaterial ); + + p->setUniformValue( id.color, material->m_color ); + p->setUniformValue( id.arc, material->m_arc ); + p->setUniformValue( id.spreadRadius, material->m_spreadRadius ); + p->setUniformValue( id.blurRadius, material->m_blurRadius ); + } + } + + private: + Uniforms id; + }; +} + +#endif + +namespace +{ + Material::Material() + { + setFlag( QSGMaterial::Blending, true ); +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + setFlag( QSGMaterial::SupportsRhiShader, true ); +#endif + } + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + + QSGMaterialShader* Material::createShader() const + { + if ( !( flags() & QSGMaterial::RhiShaderWanted ) ) + return new ShaderGL(); + + return new ShaderRhi(); + } + +#else + + QSGMaterialShader* Material::createShader( QSGRendererInterface::RenderMode ) const + { + return new ShaderRhi(); + } + +#endif + + QSGMaterialType* Material::type() const + { + static QSGMaterialType staticType; + return &staticType; + } + + int Material::compare( const QSGMaterial* const other ) const + { + auto material = static_cast< const Material* >( other ); + + if ( ( material->m_color == m_color ) + && ( material->m_arc == m_arc ) + && qFuzzyCompare( material->m_spreadRadius, m_spreadRadius ) + && qFuzzyCompare( material->m_blurRadius, m_blurRadius ) ) + { + return 0; + } + + return QSGMaterial::compare( other ); + } +} + +class QskArcShadowNodePrivate final : public QSGGeometryNodePrivate +{ + public: + QskArcShadowNodePrivate() + : geometry( QSGGeometry::defaultAttributes_TexturedPoint2D(), 4 ) + { + } + + QSGGeometry geometry; + Material material; + QRectF rect; +}; + +QskArcShadowNode::QskArcShadowNode() + : QSGGeometryNode( *new QskArcShadowNodePrivate ) +{ + Q_D( QskArcShadowNode ); + + setGeometry( &d->geometry ); + setMaterial( &d->material ); + + d->geometry.setDrawingMode( QSGGeometry::DrawTriangleStrip ); + d->material.setFlag( QSGMaterial::Blending ); +} + +QskArcShadowNode::~QskArcShadowNode() = default; + +void QskArcShadowNode::setShadowData( + const QRectF& rect, qreal spreadRadius, qreal blurRadius, + qreal startAngle, qreal spanAngle, const QColor& color ) +{ + if ( qFuzzyIsNull( spanAngle ) || color.alpha() == 0 ) + { + setBoundingRectangle( {} ); + return; + } + + Q_D( QskArcShadowNode ); + + if ( d->rect != rect ) + { + setBoundingRectangle( rect ); // bounding rectangle includig spread/blur + } + + const auto size = qMin( rect.width(), rect.height() ); + + { +#if 1 + const auto a = color.alphaF(); + const QVector4D c( color.redF() * a, color.greenF() * a, color.blueF() * a, a ); +#else + const QVector4D c( color.redF(), color.greenF(), color.blueF(), color.alphaF() ); +#endif + + if ( d->material.m_color != c ) + { + d->material.m_color = c; + markDirty( QSGNode::DirtyMaterial ); + } + } + + { + const float r = spreadRadius / size; + + if ( d->material.m_spreadRadius != r ) + { + d->material.m_spreadRadius = r; + markDirty( QSGNode::DirtyMaterial ); + } + } + + { + const float r = blurRadius / size; + + if ( d->material.m_blurRadius != r ) + { + d->material.m_blurRadius = r; + markDirty( QSGNode::DirtyMaterial ); + } + } + + { + QVector4D arc( 0.0, 0.0, 1.0, 0.0 ); + + { + const auto a1 = qDegreesToRadians( startAngle + 0.5 * spanAngle ); + const auto a2 = qDegreesToRadians( 0.5 * qAbs( spanAngle ) ); + + arc = QVector4D( ::cos( a1 ), ::sin( a1 ), ::cos( a2 ), ::sin( a2 ) ); + } + + if ( d->material.m_arc != arc ) + { + d->material.m_arc = arc; + markDirty( QSGNode::DirtyMaterial ); + } + } +} + +void QskArcShadowNode::setBoundingRectangle( const QRectF& rect ) +{ + Q_D( QskArcShadowNode ); + + if ( d->rect == rect ) + return; + + d->rect = rect; + + QSGGeometry::updateTexturedRectGeometry( + &d->geometry, d->rect, { -0.5, -0.5, 1.0, 1.0 } ); + d->geometry.markVertexDataDirty(); + + markDirty( QSGNode::DirtyGeometry ); +} diff --git a/src/nodes/QskArcShadowNode.h b/src/nodes/QskArcShadowNode.h new file mode 100644 index 00000000..5d76b9bf --- /dev/null +++ b/src/nodes/QskArcShadowNode.h @@ -0,0 +1,32 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * SPDX-License-Identifier: BSD-3-Clause + *****************************************************************************/ + +#ifndef QSK_ARC_SHADOW_NODE_H +#define QSK_ARC_SHADOW_NODE_H + +#include "QskGlobal.h" +#include + +class QskArcMetrics; +class QskShadowMetrics; + +class QskArcShadowNodePrivate; + +class QskArcShadowNode : public QSGGeometryNode +{ + public: + QskArcShadowNode(); + ~QskArcShadowNode() override; + + void setShadowData( const QRectF&, qreal spreadRadius, qreal blurRadius, + qreal startAngle, qreal spanAngle, const QColor& ); + + private: + void setBoundingRectangle( const QRectF& ); + + Q_DECLARE_PRIVATE( QskArcShadowNode ) +}; + +#endif diff --git a/src/nodes/QskBoxShadowNode.cpp b/src/nodes/QskBoxShadowNode.cpp index d94d8ad3..6f730ceb 100644 --- a/src/nodes/QskBoxShadowNode.cpp +++ b/src/nodes/QskBoxShadowNode.cpp @@ -40,7 +40,7 @@ namespace int compare( const QSGMaterial* other ) const override; - QVector2D m_aspect = QVector2D{ 1, 1 }; + QVector2D m_aspectRatio = QVector2D{ 1, 1 }; QVector4D m_radius = QVector4D{ 0, 0, 0, 0 }; QVector4D m_color = QVector4D{ 0, 0, 0, 1 }; float m_blurExtent = 0.0; @@ -91,9 +91,9 @@ namespace changed = true; } - if ( matOld == nullptr || matNew->m_aspect != matOld->m_aspect ) + if ( matOld == nullptr || matNew->m_aspectRatio != matOld->m_aspectRatio ) { - memcpy( data + 96, &matNew->m_aspect, 8 ); + memcpy( data + 96, &matNew->m_aspectRatio, 8 ); changed = true; } @@ -146,7 +146,7 @@ namespace auto p = program(); m_matrixId = p->uniformLocation( "matrix" ); - m_aspectId = p->uniformLocation( "aspect" ); + m_aspectRatioId = p->uniformLocation( "aspectRatio" ); m_opacityId = p->uniformLocation( "opacity" ); m_blurExtentId = p->uniformLocation( "blurExtent" ); m_radiusId = p->uniformLocation( "radius" ); @@ -173,7 +173,7 @@ namespace { auto material = static_cast< const Material* >( newMaterial ); - p->setUniformValue( m_aspectId, material->m_aspect ); + p->setUniformValue( m_aspectRatioId, material->m_aspectRatio ); p->setUniformValue( m_blurExtentId, material->m_blurExtent); p->setUniformValue( m_radiusId, material->m_radius ); p->setUniformValue( m_colorId, material->m_color ); @@ -183,7 +183,7 @@ namespace private: int m_matrixId = -1; int m_opacityId = -1; - int m_aspectId = -1; + int m_aspectRatioId = -1; int m_blurExtentId = -1; int m_radiusId = -1; int m_colorId = -1; @@ -231,7 +231,7 @@ int Material::compare( const QSGMaterial* other ) const auto material = static_cast< const Material* >( other ); if ( ( material->m_color == m_color ) - && ( material->m_aspect == m_aspect ) + && ( material->m_aspectRatio == m_aspectRatio ) && qFuzzyCompare(material->m_blurExtent, m_blurExtent) && qFuzzyCompare(material->m_radius, m_radius) ) { @@ -284,16 +284,16 @@ void QskBoxShadowNode::setShadowData( d->geometry.markVertexDataDirty(); markDirty( QSGNode::DirtyGeometry ); - QVector2D aspect( 1.0, 1.0 ); + QVector2D aspectRatio( 1.0, 1.0 ); if ( rect.width() >= rect.height() ) - aspect.setX( rect.width() / rect.height() ); + aspectRatio.setX( rect.width() / rect.height() ); else - aspect.setY( rect.height() / rect.width() ); + aspectRatio.setY( rect.height() / rect.width() ); - if ( d->material.m_aspect != aspect ) + if ( d->material.m_aspectRatio != aspectRatio ) { - d->material.m_aspect = aspect; + d->material.m_aspectRatio = aspectRatio; markDirty( QSGNode::DirtyMaterial ); } } diff --git a/src/nodes/shaders.qrc b/src/nodes/shaders.qrc index 82a42748..7d2d15a4 100644 --- a/src/nodes/shaders.qrc +++ b/src/nodes/shaders.qrc @@ -2,6 +2,11 @@ + shaders/arcshadow.frag + shaders/arcshadow.vert + shaders/arcshadow.frag.qsb + shaders/arcshadow.vert.qsb + shaders/boxshadow.vert.qsb shaders/boxshadow.frag.qsb shaders/boxshadow.vert diff --git a/src/nodes/shaders/arcshadow-vulkan.frag b/src/nodes/shaders/arcshadow-vulkan.frag new file mode 100644 index 00000000..49646de3 --- /dev/null +++ b/src/nodes/shaders/arcshadow-vulkan.frag @@ -0,0 +1,46 @@ +#version 440 + +layout( location = 0 ) in vec2 coord; +layout( location = 0 ) out vec4 fragColor; + +layout( std140, binding = 0 ) uniform buf +{ + mat4 matrix; + vec4 color; + + /* + arc.xy: cos/sin of the angle of the midpoint + arc.zw: cos/sin of the angle between midpoint/endpoint + */ + vec4 arc; + + float spreadRadius; + float blurRadius; + + float opacity; +} ubuf; + +mat2 rotation( vec2 v ) { return mat2( v.x, -v.y, v.y, v.x ); } + +void main() +{ + float radius = 0.5 - ubuf.blurRadius - ubuf.spreadRadius; + + float dist = abs( length( coord ) - radius ) - ubuf.spreadRadius; + + if ( ( ubuf.arc.z ) < 1.0 && ( dist < 1.0 ) ) + { + vec2 v = coord * rotation( ubuf.arc.xy ); // x-axial symmetric + v.y = abs( v.y ); + + v *= rotation ( ubuf.arc.wz ); // end point to 90° + if ( v.x < 0.0 ) + { + v.y = max( 0.0, abs( v.y - radius ) - ubuf.spreadRadius ); + dist = max( dist, length( v ) ); + } + } + + float a = 1.0 - smoothstep( 0.0, ubuf.blurRadius, dist ); + fragColor = ubuf.color * a * ubuf.opacity; +} diff --git a/src/nodes/shaders/arcshadow-vulkan.vert b/src/nodes/shaders/arcshadow-vulkan.vert new file mode 100644 index 00000000..4899db18 --- /dev/null +++ b/src/nodes/shaders/arcshadow-vulkan.vert @@ -0,0 +1,21 @@ +#version 440 + +layout( location = 0 ) in vec4 in_vertex; +layout( location = 1 ) in vec2 in_coord; +layout( location = 0 ) out vec2 coord; + +layout( std140, binding = 0 ) uniform buf +{ + mat4 matrix; + vec4 color; + vec4 arc; + float spreadRadius; + float blurRadius; + float opacity; +} ubuf; + +void main() +{ + coord = in_coord; + gl_Position = ubuf.matrix * in_vertex; +} diff --git a/src/nodes/shaders/arcshadow.frag b/src/nodes/shaders/arcshadow.frag new file mode 100644 index 00000000..738b0ed4 --- /dev/null +++ b/src/nodes/shaders/arcshadow.frag @@ -0,0 +1,37 @@ +varying lowp vec2 coord; + +uniform lowp vec4 color; +uniform lowp float spreadRadius; +uniform lowp float blurRadius; +uniform lowp float opacity; + +/* + arc.xy: cos/sin of the angle of the midpoint + arc.zw: cos/sin of the angle between midpoint/endpoint + */ +uniform lowp vec4 arc; + +mat2 rotation( vec2 v ) { return mat2( v.x, -v.y, v.y, v.x ); } + +void main() +{ + float radius = 0.5 - blurRadius - spreadRadius; + + float dist = abs( length( coord ) - radius ) - spreadRadius; + + if ( ( arc.z < 1.0 ) && ( dist < 1.0 ) ) + { + vec2 v = coord * rotation( arc.xy ); // x-axial symmetric + v.y = abs( v.y ); + + v *= rotation( arc.wz ); // end point at 90° + if ( v.x < 0.0 ) + { + v.y = max( 0.0, abs( v.y - radius ) - spreadRadius ); + dist = max( dist, length( v ) ); + } + } + + float a = 1.0 - smoothstep( 0.0, blurRadius, dist ); + gl_FragColor = color * a * opacity; +} diff --git a/src/nodes/shaders/arcshadow.frag.qsb b/src/nodes/shaders/arcshadow.frag.qsb new file mode 100644 index 00000000..1c907033 Binary files /dev/null and b/src/nodes/shaders/arcshadow.frag.qsb differ diff --git a/src/nodes/shaders/arcshadow.vert b/src/nodes/shaders/arcshadow.vert new file mode 100644 index 00000000..01850039 --- /dev/null +++ b/src/nodes/shaders/arcshadow.vert @@ -0,0 +1,12 @@ +uniform highp mat4 matrix; + +attribute highp vec4 in_vertex; +attribute mediump vec2 in_coord; + +varying mediump vec2 coord; + +void main() +{ + coord = in_coord; + gl_Position = matrix * in_vertex; +} diff --git a/src/nodes/shaders/arcshadow.vert.qsb b/src/nodes/shaders/arcshadow.vert.qsb new file mode 100644 index 00000000..ae08a617 Binary files /dev/null and b/src/nodes/shaders/arcshadow.vert.qsb differ diff --git a/src/nodes/shaders/arcshadow2qsb.sh b/src/nodes/shaders/arcshadow2qsb.sh new file mode 100755 index 00000000..d78eed3b --- /dev/null +++ b/src/nodes/shaders/arcshadow2qsb.sh @@ -0,0 +1,10 @@ +#! /bin/sh + +function qsbcompile { + qsbfile=`echo $1 | sed 's/-vulkan//'` + qsb --glsl 100es,120,150 --hlsl 50 --msl 12 -b -o ${qsbfile}.qsb $1 + # qsb --qt6 -b -o ${qsbfile}.qsb $1 +} + +qsbcompile arcshadow-vulkan.vert +qsbcompile arcshadow-vulkan.frag diff --git a/src/nodes/shaders/boxshadow-vulkan.frag b/src/nodes/shaders/boxshadow-vulkan.frag index a5d5451e..58d79062 100644 --- a/src/nodes/shaders/boxshadow-vulkan.frag +++ b/src/nodes/shaders/boxshadow-vulkan.frag @@ -8,7 +8,7 @@ layout( std140, binding = 0 ) uniform buf mat4 matrix; vec4 color; vec4 radius; - vec2 aspect; + vec2 aspectRatio; float blurExtent; float opacity; } ubuf; @@ -25,25 +25,21 @@ void main() { vec4 col = vec4(0.0); - if ( ubuf.opacity > 0.0 ) - { - const float minRadius = 0.05; + float e2 = 0.5 * ubuf.blurExtent; + float r = 2.0 * effectiveRadius( ubuf.radius, coord ); - float e2 = 0.5 * ubuf.blurExtent; - float r = 2.0 * effectiveRadius( ubuf.radius, coord ); + const float minRadius = 0.05; + float f = minRadius / max( r, minRadius ); - float f = minRadius / max( r, minRadius ); + r += e2 * f; - r += e2 * f; + vec2 d = r + ubuf.blurExtent - ubuf.aspectRatio * ( 1.0 - abs( 2.0 * coord ) ); + float l = min( max(d.x, d.y), 0.0) + length( max(d, 0.0) ); - vec2 d = r + ubuf.blurExtent - ubuf.aspect * ( 1.0 - abs( 2.0 * coord ) ); - float l = min( max(d.x, d.y), 0.0) + length( max(d, 0.0) ); + float shadow = l - r; - float shadow = l - r; - - float v = smoothstep( -e2, e2, shadow ); - col = mix( ubuf.color, vec4(0.0), v ) * ubuf.opacity; - } + float v = smoothstep( -e2, e2, shadow ); + col = mix( ubuf.color, vec4(0.0), v ) * ubuf.opacity; fragColor = col; } diff --git a/src/nodes/shaders/boxshadow-vulkan.vert b/src/nodes/shaders/boxshadow-vulkan.vert index 3a308060..0fe63bd9 100644 --- a/src/nodes/shaders/boxshadow-vulkan.vert +++ b/src/nodes/shaders/boxshadow-vulkan.vert @@ -10,7 +10,7 @@ layout( std140, binding = 0 ) uniform buf mat4 matrix; vec4 color; vec4 radius; - vec2 aspect; + vec2 aspectRatio; float blurExtent; float opacity; } ubuf; diff --git a/src/nodes/shaders/boxshadow.frag b/src/nodes/shaders/boxshadow.frag index b24ff185..61b857f7 100644 --- a/src/nodes/shaders/boxshadow.frag +++ b/src/nodes/shaders/boxshadow.frag @@ -2,7 +2,7 @@ uniform lowp float opacity; uniform lowp float blurExtent; uniform lowp vec4 radius; uniform lowp vec4 color; -uniform lowp vec2 aspect; +uniform lowp vec2 aspectRatio; varying lowp vec2 coord; @@ -18,25 +18,19 @@ void main() { lowp vec4 col = vec4(0.0); - if ( opacity > 0.0 ) - { - const lowp float minRadius = 0.05; + lowp float e2 = 0.5 * blurExtent; + lowp float r = 2.0 * effectiveRadius( radius, coord ); - lowp float e2 = 0.5 * blurExtent; - lowp float r = 2.0 * effectiveRadius( radius, coord ); + const lowp float minRadius = 0.05; + r += e2 * ( minRadius / max( r, minRadius ) ); - lowp float f = minRadius / max( r, minRadius ); + lowp vec2 d = r + blurExtent - aspectRatio * ( 1.0 - abs( 2.0 * coord ) ); + lowp float l = min( max(d.x, d.y), 0.0) + length( max(d, 0.0) ); - r += e2 * f; + lowp float shadow = l - r; - 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) ); - - lowp float shadow = l - r; - - lowp float v = smoothstep( -e2, e2, shadow ); - col = mix( color, vec4(0.0), v ) * opacity; - } + lowp float v = smoothstep( -e2, e2, shadow ); + col = mix( color, vec4(0.0), v ) * opacity; gl_FragColor = col; } diff --git a/src/nodes/shaders/boxshadow.frag.qsb b/src/nodes/shaders/boxshadow.frag.qsb index 8ecfc828..3792962e 100644 Binary files a/src/nodes/shaders/boxshadow.frag.qsb and b/src/nodes/shaders/boxshadow.frag.qsb differ diff --git a/src/nodes/shaders/boxshadow.vert.qsb b/src/nodes/shaders/boxshadow.vert.qsb index d9831605..2e1e841b 100644 Binary files a/src/nodes/shaders/boxshadow.vert.qsb and b/src/nodes/shaders/boxshadow.vert.qsb differ diff --git a/src/nodes/shaders/vulkan2qsb.sh b/src/nodes/shaders/vulkan2qsb.sh index 765f5127..27639dd4 100755 --- a/src/nodes/shaders/vulkan2qsb.sh +++ b/src/nodes/shaders/vulkan2qsb.sh @@ -6,6 +6,9 @@ function qsbcompile { # qsb --qt6 -b -o ${qsbfile}.qsb $1 } +qsbcompile arcshadow-vulkan.vert +qsbcompile arcshadow-vulkan.frag + qsbcompile boxshadow-vulkan.vert qsbcompile boxshadow-vulkan.frag