diff --git a/examples/blurredbox/BlurredBox.cpp b/examples/blurredbox/BlurredBox.cpp new file mode 100644 index 00000000..8205e0f9 --- /dev/null +++ b/examples/blurredbox/BlurredBox.cpp @@ -0,0 +1,114 @@ +#include "BlurredBox.h" +#include "BlurredBoxSkinlet.h" + +#include + +QSK_SUBCONTROL( BlurredBox, Panel ) + +class BlurredBoxSkinlet; + +BlurredBox::BlurredBox( QQuickItem* parentItem ) + : QskBox( parentItem ) +{ + setFlag( QQuickItem::ItemHasContents, true ); + setSkinlet( new BlurredBoxSkinlet() ); + setPolishOnResize( true ); +} + +BlurredBox::~BlurredBox() = default; + +void BlurredBox::setShape( const QskBoxShapeMetrics& shape ) +{ + m_shape = shape; + update(); +} + +const QskBoxShapeMetrics& BlurredBox::shape() const +{ + return m_shape; +} + +QRectF BlurredBox::rectOnScreen() const +{ + return mapRectToScene( contentsRect() ); +} + +QRectF BlurredBox::rectOfScreen() const +{ + // find root node and get its rectangle + auto* root = parentItem(); + + while ( root && root->parentItem() ) + { + root = root->parentItem(); + } + + const auto rootRect = this->window()->contentItem()->boundingRect(); + return rootRect; +} + +void BlurredBox::geometryChangeEvent( QskGeometryChangeEvent* e ) +{ + Inherited::geometryChangeEvent( e ); + update(); +} + +QRectF BlurredBox::layoutRectForSize( const QSizeF& size ) const +{ + auto padding = paddingHint( Panel ); + return { padding.left() / 2, padding.top() / 2, size.width() - padding.right(), + size.height() - padding.bottom() }; +} + +float BlurredBox::blurDirections() const +{ + return m_blurDirections; +} + +void BlurredBox::setBlurDirections( float newBlurDirections ) +{ + if ( qFuzzyCompare( m_blurDirections, newBlurDirections ) ) + { + return; + } + + m_blurDirections = newBlurDirections; + update(); + Q_EMIT blurDirectionsChanged( m_blurDirections ); +} + +float BlurredBox::blurQuality() const +{ + return m_blurQuality; +} + +void BlurredBox::setBlurQuality( float newBlurQuality ) +{ + if ( qFuzzyCompare( m_blurQuality, newBlurQuality ) ) + { + return; + } + + m_blurQuality = newBlurQuality; + update(); + Q_EMIT blurQualityChanged( m_blurQuality ); +} + +float BlurredBox::blurSize() const +{ + return m_blurSize; +} + +void BlurredBox::setBlurSize( float newBlurSize ) +{ + if ( qFuzzyCompare( m_blurSize, newBlurSize ) ) + { + return; + } + + m_blurSize = newBlurSize; + update(); + Q_EMIT blurSizeChanged( m_blurSize ); +} + +#include "moc_BlurredBox.cpp" diff --git a/examples/blurredbox/BlurredBox.h b/examples/blurredbox/BlurredBox.h new file mode 100644 index 00000000..f16277bc --- /dev/null +++ b/examples/blurredbox/BlurredBox.h @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include + +class QskGradient; + +class BlurredBox : public QskBox +{ + Q_OBJECT + Q_PROPERTY( float blurDirections READ blurDirections WRITE setBlurDirections NOTIFY + blurDirectionsChanged ) + Q_PROPERTY( float blurQuality READ blurQuality WRITE setBlurQuality NOTIFY blurQualityChanged ) + Q_PROPERTY( float blurSize READ blurSize WRITE setBlurSize NOTIFY blurSizeChanged ) + using Inherited = QskBox; + + public: + QSK_SUBCONTROLS( Panel ) + + BlurredBox( QQuickItem* parent = nullptr ); + ~BlurredBox() override; + + QRectF layoutRectForSize( const QSizeF& size ) const override; + + void setShape( const QskBoxShapeMetrics& ); + const QskBoxShapeMetrics& shape() const; + + QRectF rectOnScreen() const; + QRectF rectOfScreen() const; + + void geometryChangeEvent( QskGeometryChangeEvent* ) override; + + float blurDirections() const; + void setBlurDirections( float newBlurDirections ); + + float blurQuality() const; + void setBlurQuality( float newBlurQuality ); + + float blurSize() const; + void setBlurSize( float newBlurSize ); + + Q_SIGNALS: + void blurDirectionsChanged( float ); + void blurQualityChanged( float ); + void blurSizeChanged( float ); + + private: + QskBoxShapeMetrics m_shape; + qreal m_borderWidth = 0.0; + QColor m_borderColor = Qt::black; + float m_blurDirections = 32.0; + float m_blurQuality = 8.0; + float m_blurSize = 8.0; +}; diff --git a/examples/blurredbox/BlurredBoxMaterial.cpp b/examples/blurredbox/BlurredBoxMaterial.cpp new file mode 100644 index 00000000..20f380d5 --- /dev/null +++ b/examples/blurredbox/BlurredBoxMaterial.cpp @@ -0,0 +1,34 @@ +#include "BlurredBoxMaterial.h" +#include "BlurredBoxMaterialShader.h" + +BlurredBoxMaterial::BlurredBoxMaterial() +{ + setFlag( QSGMaterial::Blending, true ); + setFlag( QSGMaterial::SupportsRhiShader, true ); +} + +QSGMaterialShader* BlurredBoxMaterial::createShader() const +{ + return new BlurredBoxMaterialShader(); +} + +QSGMaterialType* BlurredBoxMaterial::type() const +{ + static QSGMaterialType staticType; + return &staticType; +} + +int BlurredBoxMaterial::compare( const QSGMaterial* other ) const +{ + auto material = dynamic_cast< const BlurredBoxMaterial* >( other ); + + if ( material->m_rectOfScreen != m_rectOfScreen || material->m_rectOnScreen != m_rectOnScreen || + !qFuzzyCompare( material->m_rectCornerRadii, m_rectCornerRadii ) || + material->m_opacity != m_opacity || material->m_blurDirections != m_blurDirections || + material->m_blurQuality != m_blurQuality || material->m_blurSize != m_blurSize ) + { + return 1; + } + + return QSGMaterial::compare( other ); +} diff --git a/examples/blurredbox/BlurredBoxMaterial.h b/examples/blurredbox/BlurredBoxMaterial.h new file mode 100644 index 00000000..16465bd9 --- /dev/null +++ b/examples/blurredbox/BlurredBoxMaterial.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +class BlurredBoxMaterial final : public QSGMaterial +{ + public: + BlurredBoxMaterial(); + + QSGMaterialShader* createShader() const override; + QSGMaterialType* type() const override; + + int compare( const QSGMaterial* other ) const override; + + QVector4D m_rectOfScreen{ 0, 0, 0, 0 }; + QVector4D m_rectOnScreen{ 0, 0, 0, 0 }; + QVector4D m_rectCornerRadii{ 0, 0, 0, 0 }; + float m_opacity = 1.0; + float m_blurDirections = 32.0; + float m_blurQuality = 8.0; + float m_blurSize = 8.0; +}; diff --git a/examples/blurredbox/BlurredBoxMaterialShader.cpp b/examples/blurredbox/BlurredBoxMaterialShader.cpp new file mode 100644 index 00000000..46fe3987 --- /dev/null +++ b/examples/blurredbox/BlurredBoxMaterialShader.cpp @@ -0,0 +1,62 @@ +#include "BlurredBoxMaterialShader.h" +#include "BlurredBoxMaterial.h" + +BlurredBoxMaterialShader::BlurredBoxMaterialShader() +{ + setShaderSourceFile( QOpenGLShader::Vertex, ":/shaders/blurredbox.vert" ); + setShaderSourceFile( QOpenGLShader::Fragment, ":/shaders/blurredbox.frag" ); +} + +const char* const* BlurredBoxMaterialShader::attributeNames() const +{ + static char const* const names[] = { "in_vertex", "in_coord", nullptr }; + return names; +} + +void BlurredBoxMaterialShader::initialize() +{ + QSGMaterialShader::initialize(); + + auto p = program(); + + m_matrixId = p->uniformLocation( "matrix" ); + m_rectOpacityId = p->uniformLocation( "rectOpacity" ); + m_rectCornerRadiiId = p->uniformLocation( "rectCornerRadii" ); + m_blurDirectionsId = p->uniformLocation( "blurDirections" ); + m_blurQualityId = p->uniformLocation( "blurQuality" ); + m_rectOnScreen = p->uniformLocation( "rectOnScreen" ); + m_rectOfScreen = p->uniformLocation( "rectOfScreen" ); + m_blurSizeId = p->uniformLocation( "blurSize" ); +} + +void BlurredBoxMaterialShader::updateState( + const RenderState& state, QSGMaterial* newMaterial, QSGMaterial* oldMaterial ) +{ + auto p = program(); + + if ( state.isMatrixDirty() ) + { + p->setUniformValue( m_matrixId, state.combinedMatrix() ); + } + + if ( state.isOpacityDirty() ) + { + p->setUniformValue( m_rectOpacityId, state.opacity() ); + } + + bool updateMaterial = ( oldMaterial == nullptr ) || newMaterial->compare( oldMaterial ) != 0; + + updateMaterial |= state.isCachedMaterialDataDirty(); + + if ( updateMaterial ) + { + auto material = dynamic_cast< const BlurredBoxMaterial* >( newMaterial ); + + p->setUniformValue( m_rectCornerRadiiId, material->m_rectCornerRadii ); + p->setUniformValue( m_rectOfScreen, material->m_rectOfScreen ); + p->setUniformValue( m_rectOnScreen, material->m_rectOnScreen ); + p->setUniformValue( m_blurDirectionsId, material->m_blurDirections ); + p->setUniformValue( m_blurQualityId, material->m_blurQuality ); + p->setUniformValue( m_blurSizeId, material->m_blurSize ); + } +} diff --git a/examples/blurredbox/BlurredBoxMaterialShader.h b/examples/blurredbox/BlurredBoxMaterialShader.h new file mode 100644 index 00000000..9b949470 --- /dev/null +++ b/examples/blurredbox/BlurredBoxMaterialShader.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +class BlurredBoxMaterialShader final : public QSGMaterialShader +{ + public: + BlurredBoxMaterialShader(); + char const* const* attributeNames() const override; + void initialize() override; + void updateState( const QSGMaterialShader::RenderState& state, QSGMaterial* newMaterial, + QSGMaterial* oldMaterial ) override; + + private: + int m_matrixId = -1; + int m_rectOpacityId = -1; + int m_rectCornerRadiiId = -1; + int m_rectOfScreen = -1; + int m_rectOnScreen = -1; + int m_blurDirectionsId = -1; + int m_blurQualityId = -1; + int m_blurSizeId = -1; +}; diff --git a/examples/blurredbox/BlurredBoxNode.cpp b/examples/blurredbox/BlurredBoxNode.cpp new file mode 100644 index 00000000..73275122 --- /dev/null +++ b/examples/blurredbox/BlurredBoxNode.cpp @@ -0,0 +1,74 @@ +#include "BlurredBoxNode.h" +#include "BlurredBoxMaterial.h" + +#include + +#include +#include +#include + +QSK_QT_PRIVATE_BEGIN +#include +QSK_QT_PRIVATE_END + +class BlurredBoxNodePrivate final : public QSGGeometryNodePrivate +{ + public: + BlurredBoxNodePrivate() + : geometry( QSGGeometry::defaultAttributes_TexturedPoint2D(), 4 ) + { + } + + QSGGeometry geometry; + BlurredBoxMaterial material; + QRectF rect; +}; + +BlurredBoxNode::BlurredBoxNode() + : QSGGeometryNode( *new BlurredBoxNodePrivate ) +{ + Q_D( BlurredBoxNode ); + + setGeometry( &d->geometry ); + setMaterial( &d->material ); +} + +void BlurredBoxNode::setBlurData( const QRectF& rect, const QskBoxShapeMetrics& shape, + const QRectF& rectOfScreen, const QRectF& rectOnScreen, float opacity, float blurDirections, + float blurQuality, float blurSize ) +{ + Q_D( BlurredBoxNode ); + + d->rect = rect; + + const QRectF textureRect{ rectOnScreen.x() / rectOfScreen.width(), + rectOnScreen.y() / rectOfScreen.height(), rectOnScreen.width() / rectOfScreen.width(), + rectOnScreen.height() / rectOfScreen.height() }; + + QSGGeometry::updateTexturedRectGeometry( &d->geometry, d->rect, textureRect ); + + // update screen rectangle + d->material.m_rectOfScreen = { static_cast< float >( rectOfScreen.x() ), + static_cast< float >( rectOfScreen.y() ), static_cast< float >( rectOfScreen.width() ), + static_cast< float >( rectOfScreen.height() ) }; + + // update rectangle on screen + d->material.m_rectOnScreen = { static_cast< float >( rectOnScreen.x() ), + static_cast< float >( rectOnScreen.y() ), static_cast< float >( rectOnScreen.width() ), + static_cast< float >( rectOnScreen.height() ) }; + + // update all four corner radii + d->material.m_rectCornerRadii = { static_cast< float >( + shape.radius( Qt::TopLeftCorner ).width() ), + static_cast< float >( shape.radius( Qt::TopRightCorner ).width() ), + static_cast< float >( shape.radius( Qt::BottomRightCorner ).width() ), + static_cast< float >( shape.radius( Qt::BottomLeftCorner ).width() ) }; + + d->material.m_opacity = opacity; + d->material.m_blurDirections = blurDirections; + d->material.m_blurQuality = blurQuality; + d->material.m_blurSize = blurSize; + + markDirty( QSGNode::DirtyGeometry ); + markDirty( QSGNode::DirtyMaterial ); +} diff --git a/examples/blurredbox/BlurredBoxNode.h b/examples/blurredbox/BlurredBoxNode.h new file mode 100644 index 00000000..074c44be --- /dev/null +++ b/examples/blurredbox/BlurredBoxNode.h @@ -0,0 +1,21 @@ +#pragma once + +#include + +class QColor; +class QskBoxShapeMetrics; + +class BlurredBoxNodePrivate; + +class BlurredBoxNode : public QSGGeometryNode +{ + public: + BlurredBoxNode(); + + void setBlurData( const QRectF&, const QskBoxShapeMetrics&, const QRectF& rectOfScreen, + const QRectF& rectOnScreen, float opacity, float blurDirections, float blurQuality, + float blurSize ); + + private: + Q_DECLARE_PRIVATE( BlurredBoxNode ) +}; diff --git a/examples/blurredbox/BlurredBoxSkinlet.cpp b/examples/blurredbox/BlurredBoxSkinlet.cpp new file mode 100644 index 00000000..e4f13501 --- /dev/null +++ b/examples/blurredbox/BlurredBoxSkinlet.cpp @@ -0,0 +1,43 @@ +#include "BlurredBoxSkinlet.h" +#include "BlurredBox.h" +#include "BlurredBoxNode.h" + +BlurredBoxSkinlet::BlurredBoxSkinlet() +{ + setOwnedBySkinnable( true ); + setNodeRoles( { PanelRole } ); +} + +QRectF BlurredBoxSkinlet::subControlRect( + const QskSkinnable*, const QRectF& contentsRect, QskAspect::Subcontrol ) const +{ + return contentsRect; +} + +QSGNode* BlurredBoxSkinlet::updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const +{ + const auto box = dynamic_cast< const BlurredBox* >( skinnable ); + const auto r = box->subControlRect( BlurredBox::Panel ); + + if ( r.isEmpty() ) + { + return nullptr; + } + + const auto result = QskSkinlet::updateSubNode( skinnable, nodeRole, node ); + + switch ( nodeRole ) + { + case PanelRole: + auto blurred = QskSGNode::ensureNode< BlurredBoxNode >( node ); + auto rectOfScreen = box->rectOfScreen(); + auto rectOnScreen = box->rectOnScreen(); + blurred->setBlurData( r, box->shape(), rectOfScreen, rectOnScreen, + static_cast< float >( box->opacity() ), box->blurDirections(), box->blurQuality(), + box->blurSize() ); + return blurred; + } + + return result; +} diff --git a/examples/blurredbox/BlurredBoxSkinlet.h b/examples/blurredbox/BlurredBoxSkinlet.h new file mode 100644 index 00000000..aa151092 --- /dev/null +++ b/examples/blurredbox/BlurredBoxSkinlet.h @@ -0,0 +1,19 @@ +#pragma once + +#include +#include + +class BlurredBoxSkinlet : public QskSkinlet +{ + public: + enum NodeRole + { + PanelRole + }; + + BlurredBoxSkinlet(); + QRectF subControlRect( + const QskSkinnable*, const QRectF& contentsRect, QskAspect::Subcontrol ) const override; + QSGNode* updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const override; +}; diff --git a/examples/blurredbox/MainWindow.cpp b/examples/blurredbox/MainWindow.cpp new file mode 100644 index 00000000..75519895 --- /dev/null +++ b/examples/blurredbox/MainWindow.cpp @@ -0,0 +1,205 @@ +#include "MainWindow.h" +#include "BlurredBox.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +MainWindow::MainWindow() +{ + constexpr QSize size = { 1280, 720 }; + setMinimumSize( size ); + setMaximumSize( size ); + setTitle( tr( "Blurring" ) ); + createBackground(); + + // create a centered, blurred and rounded rectangle + auto* const layout = new QskLinearBox( Qt::Vertical, contentItem() ); + layout->setMargins( 40 ); + auto* const stack = new QskStackBox( layout ); + stack->setAutoLayoutChildren( true ); + auto* const blurred = new BlurredBox( stack ); + blurred->setShape( { 40, 40, 40, 40 } ); + auto* const l = new QskLinearBox( Qt::Vertical, layout ); + stack->addItem( l ); + + // create controls to change the rectangle + l->addSpacer( 10, 1 ); + createBlurDirectionsControls( blurred, l ); + createBlurQualityControls( blurred, l ); + createBlurSizeControls( blurred, l ); + createBlurOpacityControls( blurred, l ); + createBlurCornerRadiiControls( blurred, l ); + l->addSpacer( 10, 1 ); + createShortcutNote( l ); + l->addSpacer( 10, 1 ); + return; +} + +void MainWindow::createBackground() +{ + // create a brackground image + auto* const graphic = new QskGraphicLabel( contentItem() ); + graphic->setFillMode( QskGraphicLabel::FillMode::Stretch ); + graphic->setAlignment( Qt::AlignCenter ); + + // callback for rotating through the background images + auto updateBackground = [ this, graphic ]() { + static unsigned int index = 2; + index = 1 + ( index + 1 ) % 3; + const QImage image( QString( ":/backgrounds/background%1.jpg" ).arg( index ) ); + graphic->setGraphic( QskGraphic::fromImage( image ) ); + update(); + }; + updateBackground(); + + QKeySequence keys( Qt::CTRL | Qt::Key_Space ); + QskShortcutMap::addShortcut( keys, false, contentItem(), updateBackground ); +} + +void MainWindow::createBlurDirectionsControls( BlurredBox* blurred, QskLinearBox* layout ) +{ + auto* const label = new QskTextLabel( layout ); + label->setTextColor( Qt::white ); + label->setFontRole( QskSkin::MediumFont ); + auto* const slider = new QskSlider( Qt::Horizontal, layout ); + slider->setMinimum( 16.0 ); + slider->setMaximum( 32.0 ); + connect( slider, &QskSlider::valueChanged, [ blurred, label ]( qreal value ) { + blurred->setBlurDirections( static_cast< float >( value ) ); + label->setText( QString( tr( "Blur Directions" ) + " ( %1 )" ).arg( value ) ); + } ); + slider->setValue( blurred->blurDirections() ); +} + +void MainWindow::createBlurQualityControls( BlurredBox* blurred, QskLinearBox* layout ) +{ + auto* const label = new QskTextLabel( layout ); + label->setTextColor( Qt::white ); + label->setFontRole( QskSkin::MediumFont ); + auto* const slider = new QskSlider( Qt::Horizontal, layout ); + slider->setMinimum( 4.0 ); + slider->setMaximum( 16.0 ); + connect( slider, &QskSlider::valueChanged, [ blurred, label ]( qreal value ) { + blurred->setBlurQuality( static_cast< float >( value ) ); + label->setText( QString( tr( "Blur Quality" ) + " ( %1 )" ).arg( value ) ); + } ); + slider->setValue( blurred->blurQuality() ); +} + +void MainWindow::createBlurSizeControls( BlurredBox* blurred, QskLinearBox* layout ) +{ + auto* const label = new QskTextLabel( layout ); + label->setTextColor( Qt::white ); + label->setFontRole( QskSkin::MediumFont ); + auto* const slider = new QskSlider( Qt::Horizontal, layout ); + slider->setMinimum( 4.0 ); + slider->setMaximum( 32.0 ); + connect( slider, &QskSlider::valueChanged, [ blurred, label ]( qreal value ) { + blurred->setBlurSize( static_cast< float >( value ) ); + label->setText( QString( tr( "Blur Size" ) + " ( %1 )" ).arg( value ) ); + } ); + slider->setValue( blurred->blurSize() ); +} + +void MainWindow::createBlurOpacityControls( BlurredBox* blurred, QskLinearBox* layout ) +{ + auto* const label = new QskTextLabel( layout ); + label->setTextColor( Qt::white ); + label->setFontRole( QskSkin::MediumFont ); + auto* const slider = new QskSlider( Qt::Horizontal, layout ); + slider->setMinimum( 0.0 ); + slider->setMaximum( 1.0 ); + connect( slider, &QskSlider::valueChanged, [ blurred, label ]( qreal value ) { + blurred->setOpacity( value ); + label->setText( QString( tr( "Blur Opacity" ) + " ( %1 )" ).arg( value ) ); + } ); + slider->setValue( blurred->opacity() ); +} + +void MainWindow::createBlurCornerRadiiControls( BlurredBox* blurred, QskLinearBox* layout ) +{ + auto* const label = new QskTextLabel( layout ); + label->setTextColor( Qt::white ); + label->setFontRole( QskSkin::MediumFont ); + auto* const bar = new QskSegmentedBar( Qt::Horizontal, layout ); + enum + { + TL = 0, + TR = 1, + BR = 2, + BL = 3 + }; + bar->addText( "TL" ); + bar->addText( "TR" ); + bar->addText( "BR" ); + bar->addText( "BL" ); + auto* const slider = new QskSlider( Qt::Horizontal, layout ); + slider->setMinimum( 0.0 ); + slider->setMaximum( 80.0 ); + connect( slider, &QskSlider::valueChanged, [ blurred, bar, label ]( qreal value ) { + auto shape = blurred->shape(); + const auto format = tr( "Corner Radius" ) + " ( %1 )"; + + switch ( bar->currentIndex() ) + { + case TL: + shape.setTopLeft( { value, value } ); + break; + case TR: + shape.setTopRight( { value, value } ); + break; + case BR: + shape.setBottomRight( { value, value } ); + break; + case BL: + shape.setBottomLeft( { value, value } ); + break; + } + + label->setText( format.arg( value ) ); + blurred->setShape( shape ); + blurred->update(); + } ); + connect( bar, &QskSegmentedBar::currentIndexChanged, [ blurred, slider ]( int index ) { + const auto shape = blurred->shape(); + + switch ( index ) + { + case TL: + slider->setValue( shape.topLeft().width() ); + break; + case TR: + slider->setValue( shape.topRight().width() ); + break; + case BR: + slider->setValue( shape.bottomRight().width() ); + break; + case BL: + slider->setValue( shape.bottomLeft().width() ); + break; + } + } ); + slider->setValue( blurred->shape().topLeft().width() ); + bar->setSelectedIndex( TL ); + bar->setCurrentIndex( TL ); +} + +void MainWindow::createShortcutNote( QskLinearBox* layout ) +{ + const auto text = tr( "Press 'Ctrl + Space' to change the background" ); + auto* const label = new QskTextLabel( text, layout ); + label->setTextColor( Qt::white ); + label->setFontRole( QskSkin::LargeFont ); +} + +#include "moc_MainWindow.cpp" diff --git a/examples/blurredbox/MainWindow.h b/examples/blurredbox/MainWindow.h new file mode 100644 index 00000000..792ee651 --- /dev/null +++ b/examples/blurredbox/MainWindow.h @@ -0,0 +1,23 @@ +#pragma once + +#include + +class BlurredBox; +class QskLinearBox; + +class MainWindow : public QskWindow +{ + Q_OBJECT + + public: + MainWindow(); + + private: + void createBackground(); + void createBlurDirectionsControls( BlurredBox* blurred, QskLinearBox* layout ); + void createBlurQualityControls( BlurredBox* blurred, QskLinearBox* layout ); + void createBlurSizeControls( BlurredBox* blurred, QskLinearBox* layout ); + void createBlurOpacityControls( BlurredBox* blurred, QskLinearBox* layout ); + void createBlurCornerRadiiControls( BlurredBox* blurred, QskLinearBox* layout ); + void createShortcutNote( QskLinearBox* layout ); +}; diff --git a/examples/blurredbox/backgrounds/background1.jpg b/examples/blurredbox/backgrounds/background1.jpg new file mode 100644 index 00000000..5bde134d Binary files /dev/null and b/examples/blurredbox/backgrounds/background1.jpg differ diff --git a/examples/blurredbox/backgrounds/background2.jpg b/examples/blurredbox/backgrounds/background2.jpg new file mode 100644 index 00000000..8f402729 Binary files /dev/null and b/examples/blurredbox/backgrounds/background2.jpg differ diff --git a/examples/blurredbox/backgrounds/background3.jpg b/examples/blurredbox/backgrounds/background3.jpg new file mode 100644 index 00000000..9aeee470 Binary files /dev/null and b/examples/blurredbox/backgrounds/background3.jpg differ diff --git a/examples/blurredbox/blurredbox.pro b/examples/blurredbox/blurredbox.pro new file mode 100644 index 00000000..f2b09e4f --- /dev/null +++ b/examples/blurredbox/blurredbox.pro @@ -0,0 +1,23 @@ +CONFIG += qskexample + +QT += quick_private # TODO: examples should not use private headers + +SOURCES += \ + main.cpp \ + MainWindow.cpp \ + BlurredBox.cpp \ + BlurredBoxMaterial.cpp \ + BlurredBoxMaterialShader.cpp \ + BlurredBoxNode.cpp \ + BlurredBoxSkinlet.cpp \ + +HEADERS += \ + MainWindow.h \ + BlurredBox.h \ + BlurredBoxMaterial.h \ + BlurredBoxMaterialShader.h \ + BlurredBoxNode.h \ + BlurredBoxSkinlet.h \ + +RESOURCES += \ + blurringshaders.qrc diff --git a/examples/blurredbox/blurringshaders.qrc b/examples/blurredbox/blurringshaders.qrc new file mode 100644 index 00000000..deb24b6d --- /dev/null +++ b/examples/blurredbox/blurringshaders.qrc @@ -0,0 +1,9 @@ + + + shaders/blurredbox.vert + shaders/blurredbox.frag + backgrounds/background1.jpg + backgrounds/background2.jpg + backgrounds/background3.jpg + + diff --git a/examples/blurredbox/main.cpp b/examples/blurredbox/main.cpp new file mode 100644 index 00000000..f970ae77 --- /dev/null +++ b/examples/blurredbox/main.cpp @@ -0,0 +1,35 @@ +#include + +#include +#include +#include +#include +#include +#include + +#include + +#include "MainWindow.h" + +int main( int argc, char* argv[] ) +{ +#ifdef ITEM_STATISTICS + QskObjectCounter counter( true ); +#endif + + QGuiApplication app( argc, argv ); + + qskSetup->setItemUpdateFlag( QskQuickItem::PreferRasterForTextures, true ); + + // disable default skins + qskSkinManager->setPluginPaths( QStringList() ); // no plugins + + // With CTRL-B you can rotate a couple of visual debug modes + SkinnyShortcut::enable( SkinnyShortcut::RotateSkin | SkinnyShortcut::DebugBackground | + SkinnyShortcut::DebugStatistics | SkinnyShortcut::Quit ); + + MainWindow window; + window.show(); + + return app.exec(); +} diff --git a/examples/blurredbox/shaders/blurredbox.frag b/examples/blurredbox/shaders/blurredbox.frag new file mode 100644 index 00000000..2ee3e257 --- /dev/null +++ b/examples/blurredbox/shaders/blurredbox.frag @@ -0,0 +1,84 @@ +// opacity of the rectangle +uniform lowp float rectOpacity; +// x = top left, y = top right, z = bottom right, w = bottom left +uniform lowp vec4 rectCornerRadii; +// the rectangle on the screen (x, y, width, height) +uniform lowp vec4 rectOnScreen; +// the screen's rectangle (x, y, width, height) +uniform lowp vec4 rectOfScreen; +// must be greater than 0.0! +uniform lowp float blurDirections; +// must be greater than 0.0! +uniform lowp float blurQuality; +// the radius for blurring +uniform lowp float blurSize; + +// normalized position of the fragment +varying lowp vec2 coord; + +// the texture +uniform sampler2D txr; + +lowp float effectiveRadius( in lowp vec4 radii, in lowp vec2 point ) +{ + // aliases + lowp float px = point.x * rectOfScreen.z; + lowp float py = point.y * rectOfScreen.w; + lowp float rx = rectOnScreen.x; + lowp float ry = rectOnScreen.y; + lowp float rw = rectOnScreen.z; + lowp float rh = rectOnScreen.w; + + // predicates + bool l = px >= rx && px < rx + rw / 2.0; + bool r = px >= rx + rw / 2.0 && px < rx + rw; + bool t = py >= ry && py < ry + rh / 2.0; + bool b = py >= ry + rh / 2.0 && py < ry + rh; + + if ( t && l) return radii.x; + if ( t && r) return radii.y; + if ( b && r) return radii.z; + if ( b && l) return radii.w; + return 0.0; +} + +// from https://iquilezles.org/articles/distfunctions +lowp float roundedBoxSDF(vec2 centerPosition, vec2 size, lowp float radius) +{ + return length( max( abs(centerPosition) - size + radius, 0.0) ) - radius; +} + +// source: https://www.shadertoy.com/view/Xltfzj +void main() +{ + if(rectOpacity == 0.0) + { + return; + } + + // Radius for blurring around the pixel + vec2 blurRadius = blurSize / rectOnScreen.zw; + + // Pixel colour + vec4 fragColor = texture2D(txr, coord); + + // Blur calculations + const lowp float Pi = 6.28318530718; // constant for Pi*2 + for( float d = 0.0; d < Pi; d += Pi / blurDirections) + { + for(float i = 1.0 / blurQuality; i <= 1.0; i += 1.0 / blurQuality) + { + fragColor += texture2D( txr, coord + vec2(cos(d), sin(d)) * blurRadius * i); + } + } + + lowp float cornerRadius = effectiveRadius(rectCornerRadii, coord); + lowp float edgeSoftness = 1.0f; + lowp float distance = roundedBoxSDF(coord * rectOfScreen.zw - rectOnScreen.xy - rectOnScreen.zw * 0.5f, rectOnScreen.zw * 0.5f, cornerRadius); + lowp float smoothedAlpha = 1.0f - smoothstep(0.0f, edgeSoftness * 2.0f, distance); + + // Output to screen + fragColor /= blurQuality * blurDirections - 15.0; + + gl_FragColor = mix(texture2D( txr, coord), fragColor, smoothedAlpha) * rectOpacity; +} diff --git a/examples/blurredbox/shaders/blurredbox.vert b/examples/blurredbox/shaders/blurredbox.vert new file mode 100644 index 00000000..01850039 --- /dev/null +++ b/examples/blurredbox/shaders/blurredbox.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/examples/examples.pro b/examples/examples.pro index 935bc124..8be94133 100644 --- a/examples/examples.pro +++ b/examples/examples.pro @@ -2,6 +2,7 @@ TEMPLATE = subdirs # c++ SUBDIRS += \ + blurredbox \ desktop \ gallery \ layouts \