diff --git a/examples/blurredbox/BlurredBox.cpp b/examples/blurredbox/BlurredBox.cpp new file mode 100644 index 00000000..b795d9c9 --- /dev/null +++ b/examples/blurredbox/BlurredBox.cpp @@ -0,0 +1,77 @@ +#include "BlurredBox.h" +#include "BlurredBoxSkinlet.h" +#include "BlurredBoxTextureProvider.h" + +#include +#include + +QSK_SUBCONTROL( BlurredBox, Panel ) + +class BlurredBoxSkinlet; + +BlurredBox::BlurredBox( QQuickItem* parentItem ) + : QskBox( parentItem ) +{ + setPolishOnResize( true ); +} + +BlurredBox::~BlurredBox() = default; + +void BlurredBox::geometryChangeEvent( QskGeometryChangeEvent* e ) +{ + Inherited::geometryChangeEvent( e ); + update(); +} + +float BlurredBox::blurDirections() const +{ + return m_blurDirections; +} + +void BlurredBox::setBlurDirections( float newBlurDirections ) +{ + if ( qFuzzyCompare( m_blurDirections, newBlurDirections ) ) + { + return; + } + + m_blurDirections = std::max(newBlurDirections, std::numeric_limits::min()); + 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 = std::max(newBlurQuality, std::numeric_limits::min()); + 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..150d740d --- /dev/null +++ b/examples/blurredbox/BlurredBox.h @@ -0,0 +1,43 @@ +#pragma once + +#include + +class QskGradient; +class QSGTexture; + +class BlurredBox final : 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; + + 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: + 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..b3c77fe3 --- /dev/null +++ b/examples/blurredbox/BlurredBoxMaterial.cpp @@ -0,0 +1,37 @@ +#include "BlurredBoxMaterial.h" +#include "BlurredBoxMaterialShader.h" + +BlurredBoxMaterial::BlurredBoxMaterial() +{ + setFlag( QSGMaterial::Blending, true ); +} + +QSGMaterialShader* BlurredBoxMaterial::createShader() const +{ + return new BlurredBoxMaterialShader(); +} + +QSGMaterialType* BlurredBoxMaterial::type() const +{ + static QSGMaterialType staticType; + return &staticType; +} + +int BlurredBoxMaterial::compare( const QSGMaterial* other ) const +{ + const auto* const material = dynamic_cast< const BlurredBoxMaterial* >( other ); + + if ( material->m_rectCornerRadii != m_rectCornerRadii || + material->m_rectAspect != m_rectAspect || + material->m_opacity != m_opacity || + material->m_blurDirections != m_blurDirections || + material->m_blurQuality != m_blurQuality || + material->m_blurRadius != m_blurRadius || + material->m_edgeSoftness != m_edgeSoftness || + material->m_texture.get() != m_texture.get()) + { + return 1; + } + + return QSGMaterial::compare( other ); +} diff --git a/examples/blurredbox/BlurredBoxMaterial.h b/examples/blurredbox/BlurredBoxMaterial.h new file mode 100644 index 00000000..b5184d25 --- /dev/null +++ b/examples/blurredbox/BlurredBoxMaterial.h @@ -0,0 +1,25 @@ +#pragma once + +#include +#include +#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_rectCornerRadii{ 0, 0, 0, 0 }; + QVector2D m_rectAspect {1,1}; + float m_opacity = 1.0; + float m_blurDirections = 32.0; + float m_blurQuality = 8.0; + QVector2D m_blurRadius = {1.0,1.0}; + float m_edgeSoftness = 1.0; + std::unique_ptr m_texture; +}; diff --git a/examples/blurredbox/BlurredBoxMaterialShader.cpp b/examples/blurredbox/BlurredBoxMaterialShader.cpp new file mode 100644 index 00000000..3250432c --- /dev/null +++ b/examples/blurredbox/BlurredBoxMaterialShader.cpp @@ -0,0 +1,76 @@ +#include +#include +#include +#include +#include + +#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_rectAspect = p->uniformLocation( "rectAspect" ); + m_blurDirectionsId = p->uniformLocation( "blurDirections" ); + m_blurQualityId = p->uniformLocation( "blurQuality" ); + m_blurRadiusId = p->uniformLocation( "blurRadius" ); + m_textureId = p->uniformLocation( "txr" ); + m_edgeSoftnessId = p->uniformLocation( "edgeSoftness" ); +} + +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 ) + { + return; + } + + if(const auto* const material = dynamic_cast< const BlurredBoxMaterial* >( newMaterial )) + { + p->setUniformValue( m_rectCornerRadiiId, material->m_rectCornerRadii ); + p->setUniformValue( m_rectAspect, material->m_rectAspect ); + p->setUniformValue( m_blurDirectionsId, material->m_blurDirections ); + p->setUniformValue( m_blurQualityId, material->m_blurQuality ); + p->setUniformValue( m_blurRadiusId, material->m_blurRadius ); + p->setUniformValue( m_edgeSoftnessId, material->m_edgeSoftness ); + p->setUniformValue( m_textureId, 0 /* GL_TEXTURE0 */); + + auto* const f = QOpenGLContext::currentContext()->functions(); + f->glActiveTexture(GL_TEXTURE0); + material->m_texture->bind(); + } +} diff --git a/examples/blurredbox/BlurredBoxMaterialShader.h b/examples/blurredbox/BlurredBoxMaterialShader.h new file mode 100644 index 00000000..db32317d --- /dev/null +++ b/examples/blurredbox/BlurredBoxMaterialShader.h @@ -0,0 +1,24 @@ +#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_rectAspect = -1; + int m_blurDirectionsId = -1; + int m_blurQualityId = -1; + int m_blurRadiusId = -1; + int m_textureId = -1; + int m_edgeSoftnessId = -1; +}; diff --git a/examples/blurredbox/BlurredBoxNode.cpp b/examples/blurredbox/BlurredBoxNode.cpp new file mode 100644 index 00000000..9f6c4b3a --- /dev/null +++ b/examples/blurredbox/BlurredBoxNode.cpp @@ -0,0 +1,100 @@ +#include "BlurredBoxNode.h" +#include "BlurredBoxMaterial.h" +#include "BlurredBoxTextureProvider.h" + +#include + +#include +#include +#include + +#include +#include +#include + +QSK_QT_PRIVATE_BEGIN +#include +QSK_QT_PRIVATE_END + +#include + +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 ); + setFlag(QSGNode::UsePreprocess); +} + +void BlurredBoxNode::preprocess() +{ + Q_D( BlurredBoxNode ); + if(d->material.m_texture) + { + if (auto* dynamicTexture = qobject_cast(d->material.m_texture.get() )) + { + dynamicTexture->updateTexture(); + } + } +} + +void BlurredBoxNode::setBlurData( const QRectF& rect, const QskBoxShapeMetrics& shape, + const QRectF& rectOnScreen, float opacity, float blurDirections, + float blurQuality, float blurSize, BlurredBoxTextureProvider* textureProvider) +{ + Q_D( BlurredBoxNode ); + + d->rect = rect; + + if(!d->material.m_texture) + { + d->material.m_texture = std::unique_ptr(textureProvider->texture(rectOnScreen.toRect())); + } + + QSGGeometry::updateTexturedRectGeometry( &d->geometry, d->rect, {0.0,0.0,1.0,1.0} /* texture coordinates */); + + // update all four corner radii + const auto size = std::min( d->rect.width(), d->rect.height() ); + d->material.m_rectCornerRadii = { + static_cast< float >( std::min( 1.0, shape.radius( Qt::TopLeftCorner ).width() / size)), + static_cast< float >( std::min( 1.0, shape.radius( Qt::TopRightCorner ).width() / size)), + static_cast< float >( std::min( 1.0, shape.radius( Qt::BottomRightCorner ).width() / size)), + static_cast< float >( std::min( 1.0, shape.radius( Qt::BottomLeftCorner ).width() / size)) + }; + + // update the blurring radii + d->material.m_blurRadius = { + static_cast< float >( blurSize / rectOnScreen.width()), + static_cast< float >( blurSize / rectOnScreen.height()) + }; + + // updated rectangle's aspect ratio + const auto cond = rect.width() >= rect.height(); + d->material.m_rectAspect = { + static_cast(cond ? rect.width() / rect.height() : 1.0), + static_cast(cond ? 1.0 : rect.height() / rect.width()) + }; + + d->material.m_opacity = opacity; + d->material.m_blurDirections = std::max(blurDirections, std::numeric_limits::min()); + d->material.m_blurQuality = std::max(blurQuality, std::numeric_limits::min()); + d->material.m_edgeSoftness = static_cast(1.0f / std::max(1.0, rect.width())); + + markDirty( QSGNode::DirtyGeometry ); + markDirty( QSGNode::DirtyMaterial ); +} diff --git a/examples/blurredbox/BlurredBoxNode.h b/examples/blurredbox/BlurredBoxNode.h new file mode 100644 index 00000000..9a841d77 --- /dev/null +++ b/examples/blurredbox/BlurredBoxNode.h @@ -0,0 +1,21 @@ +#pragma once + +#include +#include "BlurredBoxTextureProvider.h" + +class QskBoxShapeMetrics; +class BlurredBoxNodePrivate; + +class BlurredBoxNode final : public QSGGeometryNode +{ + public: + BlurredBoxNode(); + + void preprocess() override; + + void setBlurData( const QRectF&, const QskBoxShapeMetrics&, const QRectF& rectOnScreen, float opacity, float blurDirections, float blurQuality, + float blurSize , BlurredBoxTextureProvider* textureProvider); + + private: + Q_DECLARE_PRIVATE( BlurredBoxNode ) +}; diff --git a/examples/blurredbox/BlurredBoxSkinlet.cpp b/examples/blurredbox/BlurredBoxSkinlet.cpp new file mode 100644 index 00000000..3145a7d1 --- /dev/null +++ b/examples/blurredbox/BlurredBoxSkinlet.cpp @@ -0,0 +1,53 @@ +#include "BlurredBoxSkinlet.h" +#include "BlurredBox.h" +#include "BlurredBoxNode.h" +#include "BlurredBoxTextureProvider.h" + +#include +#include + +#include + +BlurredBoxSkinlet::BlurredBoxSkinlet(std::shared_ptr textureProvider) : m_textureProvider(std::move(textureProvider)) +{ + setNodeRoles( { PanelRole } ); +} + +BlurredBoxSkinlet::~BlurredBoxSkinlet() +{ + m_textureProvider = nullptr; +} + +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* const blurred = QskSGNode::ensureNode< BlurredBoxNode >( node ); + const auto boxShapeHint = box->boxShapeHint(BlurredBox::Panel); + const auto rectOnScreen = box->mapRectToScene( box->contentsRect() ); + blurred->setBlurData( r, boxShapeHint, rectOnScreen, + static_cast< float >( box->opacity() ), box->blurDirections(), box->blurQuality(), + box->blurSize(), m_textureProvider.get()); + return blurred; + } + + return result; +} diff --git a/examples/blurredbox/BlurredBoxSkinlet.h b/examples/blurredbox/BlurredBoxSkinlet.h new file mode 100644 index 00000000..7bd5588f --- /dev/null +++ b/examples/blurredbox/BlurredBoxSkinlet.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include "BlurredBoxTextureProvider.h" + +class BlurredBoxSkinlet final : public QskSkinlet +{ + public: + enum NodeRole + { + PanelRole + }; + + BlurredBoxSkinlet(std::shared_ptr textureProvider); + ~BlurredBoxSkinlet() override; + QRectF subControlRect( + const QskSkinnable*, const QRectF& contentsRect, QskAspect::Subcontrol ) const override; + QSGNode* updateSubNode( + const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const override; +private: + std::shared_ptr m_textureProvider; +}; diff --git a/examples/blurredbox/BlurredBoxTextureProvider.cpp b/examples/blurredbox/BlurredBoxTextureProvider.cpp new file mode 100644 index 00000000..423f8ff4 --- /dev/null +++ b/examples/blurredbox/BlurredBoxTextureProvider.cpp @@ -0,0 +1,32 @@ +#include "BlurredBoxTextureProvider.h" + +#include +#include + +#include + +BlurredBoxTextureProvider::BlurredBoxTextureProvider(QQuickWindow *window) : m_window(window), m_image(window->grabWindow()) +{ +} + +BlurredBoxTextureProvider::~BlurredBoxTextureProvider() +{ + m_window = nullptr; +} + +QSGTexture* BlurredBoxTextureProvider::texture() const +{ + return m_window->createTextureFromImage(m_image); +} + +QSGTexture *BlurredBoxTextureProvider::texture(QRect roi) const +{ + if (!m_image.rect().contains(roi)) + { + QImage black(roi.width(), roi.height(), QImage::Format_RGBA8888); + black.fill(Qt::black); + return m_window->createTextureFromImage(black); + } + const auto image = m_image.copy(roi); + return m_window->createTextureFromImage(image); +} diff --git a/examples/blurredbox/BlurredBoxTextureProvider.h b/examples/blurredbox/BlurredBoxTextureProvider.h new file mode 100644 index 00000000..cd2fc0cf --- /dev/null +++ b/examples/blurredbox/BlurredBoxTextureProvider.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +class QSGTexture; +class QQuickWindow; + +class BlurredBoxTextureProvider final : public QSGTextureProvider +{ +public: + BlurredBoxTextureProvider(QQuickWindow* window); + BlurredBoxTextureProvider(QQuickWindow* window, QImage image); + ~BlurredBoxTextureProvider() override; + QSGTexture* texture() const override; + QSGTexture* texture(QRect roi) const; +private: + QQuickWindow* m_window = nullptr; + QImage m_image; +}; diff --git a/examples/blurredbox/MainWindow.cpp b/examples/blurredbox/MainWindow.cpp new file mode 100644 index 00000000..a2e57c62 --- /dev/null +++ b/examples/blurredbox/MainWindow.cpp @@ -0,0 +1,253 @@ +#include "MainWindow.h" +#include "BlurredBox.h" +#include "BlurredBoxTextureProvider.h" +#include "BlurredBoxSkinlet.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +class BlurredBoxAnimator : public QskAnimator{ +public: + BlurredBoxAnimator(BlurredBox& blurred): m_blurred(blurred) { + + } + void advance(qreal value) override + { + m_blurred.setOpacity(value); + } + + void done() override { + delete this; + } +private: + BlurredBox& m_blurred; +}; + +enum Layers : int { BACKGROUND = 1, BLURRED = 2, CONTROLS = 3 }; + +MainWindow::MainWindow() : m_stack(new QskStackBox(contentItem())) +{ + constexpr auto width = 1280; + constexpr auto height = 720; + constexpr QSize size = { width, height }; + setMinimumSize( size ); + setMaximumSize( size ); + setTitle( tr( "Blurring" ) ); + + m_stack->setAutoLayoutChildren(true); + createBackground(); + + QskShortcutMap::addShortcut( Qt::CTRL | Qt::Key_O, false, contentItem(), [this](){ + createOverlay(); + }); +} + +void MainWindow::createBackground() +{ + // create a brackground image + auto* const graphic = new QskGraphicLabel( m_stack ); + graphic->setZ(BACKGROUND); + graphic->setFillMode( QskGraphicLabel::FillMode::Stretch ); + graphic->setAlignment( Qt::AlignCenter ); + + static int index = 0; + const QImage image( QString( ":/backgrounds/background%1.jpg" ).arg( 1 + index++ ) ); + graphic->setGraphic( QskGraphic::fromImage( image ) ); + m_stack->addItem(graphic); + update(); +} + +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, slider, [ 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, slider, [ 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( 0.0 ); + slider->setMaximum( 32.0 ); + connect( slider, &QskSlider::valueChanged, slider, [ 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, slider, [ 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, slider, [ blurred, bar, label ]( qreal value ) { + auto shape = blurred->boxShapeHint(BlurredBox::Panel); + 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->setBoxShapeHint(BlurredBox::Panel, shape ); + blurred->update(); + } ); + connect( bar, &QskSegmentedBar::currentIndexChanged, slider, [ blurred, slider ]( int index ) { + const auto shape = blurred->boxShapeHint(BlurredBox::Panel); + + 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->boxShapeHint(BlurredBox::Panel).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 ); +} + +void MainWindow::createOverlay() +{ + auto* const skinlet = new BlurredBoxSkinlet(std::make_shared(this)); + skinlet->setOwnedBySkinnable(true); + + auto* blurredLayout = new QskLinearBox(m_stack); + blurredLayout->setZ(BLURRED); + + auto* const blurred = new BlurredBox(blurredLayout); + blurred->setBoxShapeHint(BlurredBox::Panel, { 40, 40, 40, 40 }); + blurred->setBlurQuality(20); + blurred->setBlurDirections(32); + blurred->setBlurSize(16); + blurred->setSkinlet(skinlet); + blurred->setMargins(80); + + auto* const animator = new BlurredBoxAnimator(*blurred); + animator->setDuration(200); + animator->setWindow(this); + animator->start(); + + auto* const controlsLayout = new QskLinearBox( Qt::Vertical, m_stack ); + controlsLayout->setZ(CONTROLS); + controlsLayout->setMargins(80); + + // create controls to change the rectangle + controlsLayout->addSpacer( 10, 1 ); + createBlurDirectionsControls( blurred, controlsLayout ); + createBlurQualityControls( blurred, controlsLayout ); + createBlurSizeControls( blurred, controlsLayout ); + createBlurOpacityControls( blurred, controlsLayout ); + createBlurCornerRadiiControls( blurred, controlsLayout ); + controlsLayout->addSpacer( 10, 1 ); + createShortcutNote( controlsLayout ); + controlsLayout->addSpacer( 10, 1 ); + + update(); +} + +#include "moc_MainWindow.cpp" diff --git a/examples/blurredbox/MainWindow.h b/examples/blurredbox/MainWindow.h new file mode 100644 index 00000000..b31ada58 --- /dev/null +++ b/examples/blurredbox/MainWindow.h @@ -0,0 +1,27 @@ +#pragma once + +#include +#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 ); + void createOverlay(); + + QskStackBox* m_stack = nullptr; +}; 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..c7e50270 --- /dev/null +++ b/examples/blurredbox/blurredbox.pro @@ -0,0 +1,26 @@ +CONFIG += qskexample +CONFIG += opengl + +QT += quick_private opengl-private # TODO: examples should not use private headers + +SOURCES += \ + main.cpp \ + MainWindow.cpp \ + BlurredBox.cpp \ + BlurredBoxMaterial.cpp \ + BlurredBoxMaterialShader.cpp \ + BlurredBoxNode.cpp \ + BlurredBoxSkinlet.cpp \ + BlurredBoxTextureProvider.cpp + +HEADERS += \ + MainWindow.h \ + BlurredBox.h \ + BlurredBoxMaterial.h \ + BlurredBoxMaterialShader.h \ + BlurredBoxNode.h \ + BlurredBoxSkinlet.h \ + BlurredBoxTextureProvider.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..f7eedb9f --- /dev/null +++ b/examples/blurredbox/main.cpp @@ -0,0 +1,33 @@ +#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 ); + + // 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..cc9815b6 --- /dev/null +++ b/examples/blurredbox/shaders/blurredbox.frag @@ -0,0 +1,78 @@ +// 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's aspect ratio +uniform lowp vec2 rectAspect; + +// 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 vec2 blurRadius; + +// normalized position of the fragment +varying lowp vec2 coord; + +// the texture with coordinates from top left (0.0,0.0) to bottom right (1.0,1.0) +uniform sampler2D txr; + +uniform lowp float edgeSoftness; + +lowp float effectiveRadius( in lowp vec4 radii, in lowp vec2 point ) +{ + if ( point.y < 0.5 ) + return ( point.x < 0.5) ? radii.x : radii.y; + else + return ( point.x < 0.5) ? radii.z : radii.w; +} + +lowp float roundedBoxSDF(lowp vec2 centerPosition, lowp vec2 size, lowp float radius) +{ + return length( max( abs(centerPosition) - size + radius, 0.0) ) - radius; +} + +void main() +{ + if(rectOpacity == 0.0) + { + return; + } + + lowp vec2 rectAspectHalf = rectAspect / 2.0; + + // Pixel color + lowp vec4 fragColorOrig = texture2D(txr, coord); + lowp vec4 fragColor = fragColorOrig; + + // Blur calculations + const lowp float Pi = 6.28318530718; // constant for Pi*2 + for(lowp float d = 0.0; d < Pi; d += Pi / blurDirections) + { + lowp vec2 offset = vec2(cos(d), sin(d)); + for(lowp float i = 1.0 / blurQuality; i <= 1.0; i += 1.0 / blurQuality) + { + fragColor += texture2D( txr, coord + offset * blurRadius * i); + } + } + + // Current corner radius + lowp float cornerRadius = effectiveRadius(rectCornerRadii, coord); + + // Calculate distance to edge. + lowp float distance = roundedBoxSDF(coord * rectAspect - rectAspectHalf, rectAspectHalf, cornerRadius); + + // Smooth the result (free antialiasing). + lowp float smoothedAlpha = 1.0 - smoothstep(0.0, 2.0 * edgeSoftness, distance); + + // Output to screen + fragColor /= blurQuality * blurDirections - 15.0; + + // Mix the original pixel coolor with the blurred + gl_FragColor = mix(fragColorOrig, vec4(fragColor.xyz, 1.0), smoothedAlpha) * rectOpacity; +} + +// further reading +// - https://iquilezles.org/articles/distfunctions +// - https://www.shadertoy.com/view/Xltfzj 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 \