diff --git a/examples/blurredbox/BlurredBox.cpp b/examples/blurredbox/BlurredBox.cpp index 25a185fd..b795d9c9 100644 --- a/examples/blurredbox/BlurredBox.cpp +++ b/examples/blurredbox/BlurredBox.cpp @@ -1,7 +1,9 @@ #include "BlurredBox.h" #include "BlurredBoxSkinlet.h" +#include "BlurredBoxTextureProvider.h" #include +#include QSK_SUBCONTROL( BlurredBox, Panel ) @@ -10,33 +12,11 @@ class BlurredBoxSkinlet; BlurredBox::BlurredBox( QQuickItem* parentItem ) : QskBox( parentItem ) { - auto* const skinlet = new BlurredBoxSkinlet(); - skinlet->setOwnedBySkinnable( true ); - setSkinlet( skinlet ); setPolishOnResize( true ); } BlurredBox::~BlurredBox() = default; -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 ); @@ -55,7 +35,7 @@ void BlurredBox::setBlurDirections( float newBlurDirections ) return; } - m_blurDirections = newBlurDirections; + m_blurDirections = std::max(newBlurDirections, std::numeric_limits::min()); update(); Q_EMIT blurDirectionsChanged( m_blurDirections ); } @@ -72,7 +52,7 @@ void BlurredBox::setBlurQuality( float newBlurQuality ) return; } - m_blurQuality = newBlurQuality; + m_blurQuality = std::max(newBlurQuality, std::numeric_limits::min()); update(); Q_EMIT blurQualityChanged( m_blurQuality ); } diff --git a/examples/blurredbox/BlurredBox.h b/examples/blurredbox/BlurredBox.h index 18d9641f..150d740d 100644 --- a/examples/blurredbox/BlurredBox.h +++ b/examples/blurredbox/BlurredBox.h @@ -1,12 +1,11 @@ #pragma once #include -#include -#include class QskGradient; +class QSGTexture; -class BlurredBox : public QskBox +class BlurredBox final : public QskBox { Q_OBJECT Q_PROPERTY( float blurDirections READ blurDirections WRITE setBlurDirections NOTIFY @@ -21,9 +20,6 @@ class BlurredBox : public QskBox BlurredBox( QQuickItem* parent = nullptr ); ~BlurredBox() override; - QRectF rectOnScreen() const; - QRectF rectOfScreen() const; - void geometryChangeEvent( QskGeometryChangeEvent* ) override; float blurDirections() const; diff --git a/examples/blurredbox/BlurredBoxMaterial.cpp b/examples/blurredbox/BlurredBoxMaterial.cpp index 20f380d5..b3c77fe3 100644 --- a/examples/blurredbox/BlurredBoxMaterial.cpp +++ b/examples/blurredbox/BlurredBoxMaterial.cpp @@ -4,7 +4,6 @@ BlurredBoxMaterial::BlurredBoxMaterial() { setFlag( QSGMaterial::Blending, true ); - setFlag( QSGMaterial::SupportsRhiShader, true ); } QSGMaterialShader* BlurredBoxMaterial::createShader() const @@ -20,12 +19,16 @@ QSGMaterialType* BlurredBoxMaterial::type() const int BlurredBoxMaterial::compare( const QSGMaterial* other ) const { - auto material = dynamic_cast< const BlurredBoxMaterial* >( other ); + const auto* const 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 ) + 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; } diff --git a/examples/blurredbox/BlurredBoxMaterial.h b/examples/blurredbox/BlurredBoxMaterial.h index 16465bd9..b5184d25 100644 --- a/examples/blurredbox/BlurredBoxMaterial.h +++ b/examples/blurredbox/BlurredBoxMaterial.h @@ -1,6 +1,8 @@ #pragma once #include +#include +#include class BlurredBoxMaterial final : public QSGMaterial { @@ -12,11 +14,12 @@ class BlurredBoxMaterial final : public QSGMaterial 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 }; + QVector2D m_rectAspect {1,1}; float m_opacity = 1.0; float m_blurDirections = 32.0; float m_blurQuality = 8.0; - float m_blurSize = 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 index 46fe3987..3250432c 100644 --- a/examples/blurredbox/BlurredBoxMaterialShader.cpp +++ b/examples/blurredbox/BlurredBoxMaterialShader.cpp @@ -1,3 +1,9 @@ +#include +#include +#include +#include +#include + #include "BlurredBoxMaterialShader.h" #include "BlurredBoxMaterial.h" @@ -22,11 +28,12 @@ void BlurredBoxMaterialShader::initialize() 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_rectOnScreen = p->uniformLocation( "rectOnScreen" ); - m_rectOfScreen = p->uniformLocation( "rectOfScreen" ); - m_blurSizeId = p->uniformLocation( "blurSize" ); + m_blurQualityId = p->uniformLocation( "blurQuality" ); + m_blurRadiusId = p->uniformLocation( "blurRadius" ); + m_textureId = p->uniformLocation( "txr" ); + m_edgeSoftnessId = p->uniformLocation( "edgeSoftness" ); } void BlurredBoxMaterialShader::updateState( @@ -45,18 +52,25 @@ void BlurredBoxMaterialShader::updateState( } bool updateMaterial = ( oldMaterial == nullptr ) || newMaterial->compare( oldMaterial ) != 0; - updateMaterial |= state.isCachedMaterialDataDirty(); - if ( updateMaterial ) + if ( !updateMaterial ) { - auto material = dynamic_cast< const BlurredBoxMaterial* >( newMaterial ); + return; + } + if(const auto* const 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_rectAspect, material->m_rectAspect ); p->setUniformValue( m_blurDirectionsId, material->m_blurDirections ); p->setUniformValue( m_blurQualityId, material->m_blurQuality ); - p->setUniformValue( m_blurSizeId, material->m_blurSize ); + 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 index 9b949470..db32317d 100644 --- a/examples/blurredbox/BlurredBoxMaterialShader.h +++ b/examples/blurredbox/BlurredBoxMaterialShader.h @@ -15,9 +15,10 @@ class BlurredBoxMaterialShader final : public QSGMaterialShader int m_matrixId = -1; int m_rectOpacityId = -1; int m_rectCornerRadiiId = -1; - int m_rectOfScreen = -1; - int m_rectOnScreen = -1; + int m_rectAspect = -1; int m_blurDirectionsId = -1; int m_blurQualityId = -1; - int m_blurSizeId = -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 index 73275122..9f6c4b3a 100644 --- a/examples/blurredbox/BlurredBoxNode.cpp +++ b/examples/blurredbox/BlurredBoxNode.cpp @@ -1,5 +1,6 @@ #include "BlurredBoxNode.h" #include "BlurredBoxMaterial.h" +#include "BlurredBoxTextureProvider.h" #include @@ -7,10 +8,16 @@ #include #include +#include +#include +#include + QSK_QT_PRIVATE_BEGIN #include QSK_QT_PRIVATE_END +#include + class BlurredBoxNodePrivate final : public QSGGeometryNodePrivate { public: @@ -31,43 +38,62 @@ BlurredBoxNode::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& rectOfScreen, const QRectF& rectOnScreen, float opacity, float blurDirections, - float blurQuality, float blurSize ) + const QRectF& rectOnScreen, float opacity, float blurDirections, + float blurQuality, float blurSize, BlurredBoxTextureProvider* textureProvider) { 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() }; + if(!d->material.m_texture) + { + d->material.m_texture = std::unique_ptr(textureProvider->texture(rectOnScreen.toRect())); + } - 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() ) }; + QSGGeometry::updateTexturedRectGeometry( &d->geometry, d->rect, {0.0,0.0,1.0,1.0} /* texture coordinates */); // 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() ) }; + 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 = blurDirections; - d->material.m_blurQuality = blurQuality; - d->material.m_blurSize = blurSize; + 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 index 074c44be..9a841d77 100644 --- a/examples/blurredbox/BlurredBoxNode.h +++ b/examples/blurredbox/BlurredBoxNode.h @@ -1,20 +1,20 @@ #pragma once #include +#include "BlurredBoxTextureProvider.h" -class QColor; class QskBoxShapeMetrics; - class BlurredBoxNodePrivate; -class BlurredBoxNode : public QSGGeometryNode +class BlurredBoxNode final : public QSGGeometryNode { public: BlurredBoxNode(); - void setBlurData( const QRectF&, const QskBoxShapeMetrics&, const QRectF& rectOfScreen, - const QRectF& rectOnScreen, float opacity, float blurDirections, float blurQuality, - float blurSize ); + 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 index 7de2ac94..3145a7d1 100644 --- a/examples/blurredbox/BlurredBoxSkinlet.cpp +++ b/examples/blurredbox/BlurredBoxSkinlet.cpp @@ -1,12 +1,23 @@ #include "BlurredBoxSkinlet.h" #include "BlurredBox.h" #include "BlurredBoxNode.h" +#include "BlurredBoxTextureProvider.h" -BlurredBoxSkinlet::BlurredBoxSkinlet() +#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 { @@ -30,12 +41,11 @@ QSGNode* BlurredBoxSkinlet::updateSubNode( { case PanelRole: auto* const blurred = QskSGNode::ensureNode< BlurredBoxNode >( node ); - const auto rectOfScreen = box->rectOfScreen(); - const auto rectOnScreen = box->rectOnScreen(); const auto boxShapeHint = box->boxShapeHint(BlurredBox::Panel); - blurred->setBlurData( r, boxShapeHint, rectOfScreen, rectOnScreen, + const auto rectOnScreen = box->mapRectToScene( box->contentsRect() ); + blurred->setBlurData( r, boxShapeHint, rectOnScreen, static_cast< float >( box->opacity() ), box->blurDirections(), box->blurQuality(), - box->blurSize() ); + box->blurSize(), m_textureProvider.get()); return blurred; } diff --git a/examples/blurredbox/BlurredBoxSkinlet.h b/examples/blurredbox/BlurredBoxSkinlet.h index aa151092..7bd5588f 100644 --- a/examples/blurredbox/BlurredBoxSkinlet.h +++ b/examples/blurredbox/BlurredBoxSkinlet.h @@ -1,9 +1,10 @@ #pragma once -#include #include +#include +#include "BlurredBoxTextureProvider.h" -class BlurredBoxSkinlet : public QskSkinlet +class BlurredBoxSkinlet final : public QskSkinlet { public: enum NodeRole @@ -11,9 +12,12 @@ class BlurredBoxSkinlet : public QskSkinlet PanelRole }; - BlurredBoxSkinlet(); + 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 index 4c92a23a..a2e57c62 100644 --- a/examples/blurredbox/MainWindow.cpp +++ b/examples/blurredbox/MainWindow.cpp @@ -1,5 +1,7 @@ #include "MainWindow.h" #include "BlurredBox.h" +#include "BlurredBoxTextureProvider.h" +#include "BlurredBoxSkinlet.h" #include #include @@ -7,64 +9,70 @@ #include #include +#include #include #include #include #include #include #include +#include +#include +#include +#include +#include -MainWindow::MainWindow() +#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 QSize size = { 1280, 720 }; + 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(); - // 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->setBlurSize( 20.0 ); - blurred->setBoxShapeHint(BlurredBox::Panel, { 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; + QskShortcutMap::addShortcut( Qt::CTRL | Qt::Key_O, false, contentItem(), [this](){ + createOverlay(); + }); } void MainWindow::createBackground() { // create a brackground image - auto* const graphic = new QskGraphicLabel( contentItem() ); + auto* const graphic = new QskGraphicLabel( m_stack ); + graphic->setZ(BACKGROUND); 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 ); + 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 ) @@ -103,7 +111,7 @@ void MainWindow::createBlurSizeControls( BlurredBox* blurred, QskLinearBox* layo label->setTextColor( Qt::white ); label->setFontRole( QskSkin::MediumFont ); auto* const slider = new QskSlider( Qt::Horizontal, layout ); - slider->setMinimum( 4.0 ); + slider->setMinimum( 0.0 ); slider->setMaximum( 32.0 ); connect( slider, &QskSlider::valueChanged, slider, [ blurred, label ]( qreal value ) { blurred->setBlurSize( static_cast< float >( value ) ); @@ -203,4 +211,43 @@ void MainWindow::createShortcutNote( QskLinearBox* layout ) 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 index 792ee651..b31ada58 100644 --- a/examples/blurredbox/MainWindow.h +++ b/examples/blurredbox/MainWindow.h @@ -1,6 +1,7 @@ #pragma once #include +#include class BlurredBox; class QskLinearBox; @@ -20,4 +21,7 @@ class MainWindow : public QskWindow 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/blurredbox.pro b/examples/blurredbox/blurredbox.pro index f2b09e4f..c7e50270 100644 --- a/examples/blurredbox/blurredbox.pro +++ b/examples/blurredbox/blurredbox.pro @@ -1,6 +1,7 @@ CONFIG += qskexample +CONFIG += opengl -QT += quick_private # TODO: examples should not use private headers +QT += quick_private opengl-private # TODO: examples should not use private headers SOURCES += \ main.cpp \ @@ -10,6 +11,7 @@ SOURCES += \ BlurredBoxMaterialShader.cpp \ BlurredBoxNode.cpp \ BlurredBoxSkinlet.cpp \ + BlurredBoxTextureProvider.cpp HEADERS += \ MainWindow.h \ @@ -18,6 +20,7 @@ HEADERS += \ BlurredBoxMaterialShader.h \ BlurredBoxNode.h \ BlurredBoxSkinlet.h \ + BlurredBoxTextureProvider.h RESOURCES += \ blurringshaders.qrc diff --git a/examples/blurredbox/shaders/blurredbox.frag b/examples/blurredbox/shaders/blurredbox.frag index 2ee3e257..cc9815b6 100644 --- a/examples/blurredbox/shaders/blurredbox.frag +++ b/examples/blurredbox/shaders/blurredbox.frag @@ -2,53 +2,37 @@ 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; +// 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 float blurSize; +uniform lowp vec2 blurRadius; // normalized position of the fragment varying lowp vec2 coord; -// the texture +// 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 ) -{ - // 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; +{ + if ( point.y < 0.5 ) + return ( point.x < 0.5) ? radii.x : radii.y; + else + return ( point.x < 0.5) ? radii.z : radii.w; } -// from https://iquilezles.org/articles/distfunctions -lowp float roundedBoxSDF(vec2 centerPosition, vec2 size, lowp float radius) +lowp float roundedBoxSDF(lowp vec2 centerPosition, lowp 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) @@ -56,29 +40,39 @@ void main() return; } - // Radius for blurring around the pixel - vec2 blurRadius = blurSize / rectOnScreen.zw; + lowp vec2 rectAspectHalf = rectAspect / 2.0; - // Pixel colour - vec4 fragColor = texture2D(txr, coord); + // 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( float d = 0.0; d < Pi; d += Pi / blurDirections) + for(lowp float d = 0.0; d < Pi; d += Pi / blurDirections) { - for(float i = 1.0 / blurQuality; i <= 1.0; i += 1.0 / blurQuality) + 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 + vec2(cos(d), sin(d)) * blurRadius * i); + fragColor += texture2D( txr, coord + offset * blurRadius * i); } } + // Current corner radius 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); + + // 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; - gl_FragColor = mix(texture2D( txr, coord), fragColor, smoothedAlpha) * rectOpacity; + // 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