Using relative coordinates; Using screenshot as texture

This commit is contained in:
Rick Vogel 2022-09-14 13:04:25 +02:00
parent 738b484a0b
commit 167bf588bd
16 changed files with 306 additions and 169 deletions

View File

@ -1,7 +1,9 @@
#include "BlurredBox.h" #include "BlurredBox.h"
#include "BlurredBoxSkinlet.h" #include "BlurredBoxSkinlet.h"
#include "BlurredBoxTextureProvider.h"
#include <QQuickWindow> #include <QQuickWindow>
#include <limits>
QSK_SUBCONTROL( BlurredBox, Panel ) QSK_SUBCONTROL( BlurredBox, Panel )
@ -10,33 +12,11 @@ class BlurredBoxSkinlet;
BlurredBox::BlurredBox( QQuickItem* parentItem ) BlurredBox::BlurredBox( QQuickItem* parentItem )
: QskBox( parentItem ) : QskBox( parentItem )
{ {
auto* const skinlet = new BlurredBoxSkinlet();
skinlet->setOwnedBySkinnable( true );
setSkinlet( skinlet );
setPolishOnResize( true ); setPolishOnResize( true );
} }
BlurredBox::~BlurredBox() = default; 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 ) void BlurredBox::geometryChangeEvent( QskGeometryChangeEvent* e )
{ {
Inherited::geometryChangeEvent( e ); Inherited::geometryChangeEvent( e );
@ -55,7 +35,7 @@ void BlurredBox::setBlurDirections( float newBlurDirections )
return; return;
} }
m_blurDirections = newBlurDirections; m_blurDirections = std::max(newBlurDirections, std::numeric_limits<float>::min());
update(); update();
Q_EMIT blurDirectionsChanged( m_blurDirections ); Q_EMIT blurDirectionsChanged( m_blurDirections );
} }
@ -72,7 +52,7 @@ void BlurredBox::setBlurQuality( float newBlurQuality )
return; return;
} }
m_blurQuality = newBlurQuality; m_blurQuality = std::max(newBlurQuality, std::numeric_limits<float>::min());
update(); update();
Q_EMIT blurQualityChanged( m_blurQuality ); Q_EMIT blurQualityChanged( m_blurQuality );
} }

View File

@ -1,12 +1,11 @@
#pragma once #pragma once
#include <QskBox.h> #include <QskBox.h>
#include <QskBoxShapeMetrics.h>
#include <QskShadowMetrics.h>
class QskGradient; class QskGradient;
class QSGTexture;
class BlurredBox : public QskBox class BlurredBox final : public QskBox
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY( float blurDirections READ blurDirections WRITE setBlurDirections NOTIFY Q_PROPERTY( float blurDirections READ blurDirections WRITE setBlurDirections NOTIFY
@ -21,9 +20,6 @@ class BlurredBox : public QskBox
BlurredBox( QQuickItem* parent = nullptr ); BlurredBox( QQuickItem* parent = nullptr );
~BlurredBox() override; ~BlurredBox() override;
QRectF rectOnScreen() const;
QRectF rectOfScreen() const;
void geometryChangeEvent( QskGeometryChangeEvent* ) override; void geometryChangeEvent( QskGeometryChangeEvent* ) override;
float blurDirections() const; float blurDirections() const;

View File

@ -4,7 +4,6 @@
BlurredBoxMaterial::BlurredBoxMaterial() BlurredBoxMaterial::BlurredBoxMaterial()
{ {
setFlag( QSGMaterial::Blending, true ); setFlag( QSGMaterial::Blending, true );
setFlag( QSGMaterial::SupportsRhiShader, true );
} }
QSGMaterialShader* BlurredBoxMaterial::createShader() const QSGMaterialShader* BlurredBoxMaterial::createShader() const
@ -20,12 +19,16 @@ QSGMaterialType* BlurredBoxMaterial::type() const
int BlurredBoxMaterial::compare( const QSGMaterial* other ) 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 || if ( material->m_rectCornerRadii != m_rectCornerRadii ||
!qFuzzyCompare( material->m_rectCornerRadii, m_rectCornerRadii ) || material->m_rectAspect != m_rectAspect ||
material->m_opacity != m_opacity || material->m_blurDirections != m_blurDirections || material->m_opacity != m_opacity ||
material->m_blurQuality != m_blurQuality || material->m_blurSize != m_blurSize ) 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 1;
} }

View File

@ -1,6 +1,8 @@
#pragma once #pragma once
#include <QSGMaterial> #include <QSGMaterial>
#include <QSGTexture>
#include <memory>
class BlurredBoxMaterial final : public QSGMaterial class BlurredBoxMaterial final : public QSGMaterial
{ {
@ -12,11 +14,12 @@ class BlurredBoxMaterial final : public QSGMaterial
int compare( const QSGMaterial* other ) 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 }; QVector4D m_rectCornerRadii{ 0, 0, 0, 0 };
QVector2D m_rectAspect {1,1};
float m_opacity = 1.0; float m_opacity = 1.0;
float m_blurDirections = 32.0; float m_blurDirections = 32.0;
float m_blurQuality = 8.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<QSGTexture> m_texture;
}; };

View File

@ -1,3 +1,9 @@
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QOpenGLTexture>
#include <QImage>
#include <QSGTexture>
#include "BlurredBoxMaterialShader.h" #include "BlurredBoxMaterialShader.h"
#include "BlurredBoxMaterial.h" #include "BlurredBoxMaterial.h"
@ -22,11 +28,12 @@ void BlurredBoxMaterialShader::initialize()
m_matrixId = p->uniformLocation( "matrix" ); m_matrixId = p->uniformLocation( "matrix" );
m_rectOpacityId = p->uniformLocation( "rectOpacity" ); m_rectOpacityId = p->uniformLocation( "rectOpacity" );
m_rectCornerRadiiId = p->uniformLocation( "rectCornerRadii" ); m_rectCornerRadiiId = p->uniformLocation( "rectCornerRadii" );
m_rectAspect = p->uniformLocation( "rectAspect" );
m_blurDirectionsId = p->uniformLocation( "blurDirections" ); m_blurDirectionsId = p->uniformLocation( "blurDirections" );
m_blurQualityId = p->uniformLocation( "blurQuality" ); m_blurQualityId = p->uniformLocation( "blurQuality" );
m_rectOnScreen = p->uniformLocation( "rectOnScreen" ); m_blurRadiusId = p->uniformLocation( "blurRadius" );
m_rectOfScreen = p->uniformLocation( "rectOfScreen" ); m_textureId = p->uniformLocation( "txr" );
m_blurSizeId = p->uniformLocation( "blurSize" ); m_edgeSoftnessId = p->uniformLocation( "edgeSoftness" );
} }
void BlurredBoxMaterialShader::updateState( void BlurredBoxMaterialShader::updateState(
@ -45,18 +52,25 @@ void BlurredBoxMaterialShader::updateState(
} }
bool updateMaterial = ( oldMaterial == nullptr ) || newMaterial->compare( oldMaterial ) != 0; bool updateMaterial = ( oldMaterial == nullptr ) || newMaterial->compare( oldMaterial ) != 0;
updateMaterial |= state.isCachedMaterialDataDirty(); 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_rectCornerRadiiId, material->m_rectCornerRadii );
p->setUniformValue( m_rectOfScreen, material->m_rectOfScreen ); p->setUniformValue( m_rectAspect, material->m_rectAspect );
p->setUniformValue( m_rectOnScreen, material->m_rectOnScreen );
p->setUniformValue( m_blurDirectionsId, material->m_blurDirections ); p->setUniformValue( m_blurDirectionsId, material->m_blurDirections );
p->setUniformValue( m_blurQualityId, material->m_blurQuality ); 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();
} }
} }

View File

@ -15,9 +15,10 @@ class BlurredBoxMaterialShader final : public QSGMaterialShader
int m_matrixId = -1; int m_matrixId = -1;
int m_rectOpacityId = -1; int m_rectOpacityId = -1;
int m_rectCornerRadiiId = -1; int m_rectCornerRadiiId = -1;
int m_rectOfScreen = -1; int m_rectAspect = -1;
int m_rectOnScreen = -1;
int m_blurDirectionsId = -1; int m_blurDirectionsId = -1;
int m_blurQualityId = -1; int m_blurQualityId = -1;
int m_blurSizeId = -1; int m_blurRadiusId = -1;
int m_textureId = -1;
int m_edgeSoftnessId = -1;
}; };

View File

@ -1,5 +1,6 @@
#include "BlurredBoxNode.h" #include "BlurredBoxNode.h"
#include "BlurredBoxMaterial.h" #include "BlurredBoxMaterial.h"
#include "BlurredBoxTextureProvider.h"
#include <QskBoxShapeMetrics.h> #include <QskBoxShapeMetrics.h>
@ -7,10 +8,16 @@
#include <qsgmaterial.h> #include <qsgmaterial.h>
#include <qsgmaterialshader.h> #include <qsgmaterialshader.h>
#include <QOpenGLContext>
#include <QOpenGLFunctions>
#include <QQuickWindow>
QSK_QT_PRIVATE_BEGIN QSK_QT_PRIVATE_BEGIN
#include <private/qsgnode_p.h> #include <private/qsgnode_p.h>
QSK_QT_PRIVATE_END QSK_QT_PRIVATE_END
#include <limits>
class BlurredBoxNodePrivate final : public QSGGeometryNodePrivate class BlurredBoxNodePrivate final : public QSGGeometryNodePrivate
{ {
public: public:
@ -31,43 +38,62 @@ BlurredBoxNode::BlurredBoxNode()
setGeometry( &d->geometry ); setGeometry( &d->geometry );
setMaterial( &d->material ); setMaterial( &d->material );
setFlag(QSGNode::UsePreprocess);
}
void BlurredBoxNode::preprocess()
{
Q_D( BlurredBoxNode );
if(d->material.m_texture)
{
if (auto* dynamicTexture = qobject_cast<QSGDynamicTexture*>(d->material.m_texture.get() ))
{
dynamicTexture->updateTexture();
}
}
} }
void BlurredBoxNode::setBlurData( const QRectF& rect, const QskBoxShapeMetrics& shape, void BlurredBoxNode::setBlurData( const QRectF& rect, const QskBoxShapeMetrics& shape,
const QRectF& rectOfScreen, const QRectF& rectOnScreen, float opacity, float blurDirections, const QRectF& rectOnScreen, float opacity, float blurDirections,
float blurQuality, float blurSize ) float blurQuality, float blurSize, BlurredBoxTextureProvider* textureProvider)
{ {
Q_D( BlurredBoxNode ); Q_D( BlurredBoxNode );
d->rect = rect; d->rect = rect;
const QRectF textureRect{ rectOnScreen.x() / rectOfScreen.width(), if(!d->material.m_texture)
rectOnScreen.y() / rectOfScreen.height(), rectOnScreen.width() / rectOfScreen.width(), {
rectOnScreen.height() / rectOfScreen.height() }; d->material.m_texture = std::unique_ptr<QSGTexture>(textureProvider->texture(rectOnScreen.toRect()));
}
QSGGeometry::updateTexturedRectGeometry( &d->geometry, d->rect, textureRect ); QSGGeometry::updateTexturedRectGeometry( &d->geometry, d->rect, {0.0,0.0,1.0,1.0} /* texture coordinates */);
// 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 // update all four corner radii
d->material.m_rectCornerRadii = { static_cast< float >( const auto size = std::min( d->rect.width(), d->rect.height() );
shape.radius( Qt::TopLeftCorner ).width() ), d->material.m_rectCornerRadii = {
static_cast< float >( shape.radius( Qt::TopRightCorner ).width() ), static_cast< float >( std::min( 1.0, shape.radius( Qt::TopLeftCorner ).width() / size)),
static_cast< float >( shape.radius( Qt::BottomRightCorner ).width() ), static_cast< float >( std::min( 1.0, shape.radius( Qt::TopRightCorner ).width() / size)),
static_cast< float >( shape.radius( Qt::BottomLeftCorner ).width() ) }; 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<float>(cond ? rect.width() / rect.height() : 1.0),
static_cast<float>(cond ? 1.0 : rect.height() / rect.width())
};
d->material.m_opacity = opacity; d->material.m_opacity = opacity;
d->material.m_blurDirections = blurDirections; d->material.m_blurDirections = std::max(blurDirections, std::numeric_limits<float>::min());
d->material.m_blurQuality = blurQuality; d->material.m_blurQuality = std::max(blurQuality, std::numeric_limits<float>::min());
d->material.m_blurSize = blurSize; d->material.m_edgeSoftness = static_cast<float>(1.0f / std::max(1.0, rect.width()));
markDirty( QSGNode::DirtyGeometry ); markDirty( QSGNode::DirtyGeometry );
markDirty( QSGNode::DirtyMaterial ); markDirty( QSGNode::DirtyMaterial );

View File

@ -1,20 +1,20 @@
#pragma once #pragma once
#include <QSGGeometryNode> #include <QSGGeometryNode>
#include "BlurredBoxTextureProvider.h"
class QColor;
class QskBoxShapeMetrics; class QskBoxShapeMetrics;
class BlurredBoxNodePrivate; class BlurredBoxNodePrivate;
class BlurredBoxNode : public QSGGeometryNode class BlurredBoxNode final : public QSGGeometryNode
{ {
public: public:
BlurredBoxNode(); BlurredBoxNode();
void setBlurData( const QRectF&, const QskBoxShapeMetrics&, const QRectF& rectOfScreen, void preprocess() override;
const QRectF& rectOnScreen, float opacity, float blurDirections, float blurQuality,
float blurSize ); void setBlurData( const QRectF&, const QskBoxShapeMetrics&, const QRectF& rectOnScreen, float opacity, float blurDirections, float blurQuality,
float blurSize , BlurredBoxTextureProvider* textureProvider);
private: private:
Q_DECLARE_PRIVATE( BlurredBoxNode ) Q_DECLARE_PRIVATE( BlurredBoxNode )

View File

@ -1,12 +1,23 @@
#include "BlurredBoxSkinlet.h" #include "BlurredBoxSkinlet.h"
#include "BlurredBox.h" #include "BlurredBox.h"
#include "BlurredBoxNode.h" #include "BlurredBoxNode.h"
#include "BlurredBoxTextureProvider.h"
BlurredBoxSkinlet::BlurredBoxSkinlet() #include <QskSGNode.h>
#include <QskBoxShapeMetrics.h>
#include <utility>
BlurredBoxSkinlet::BlurredBoxSkinlet(std::shared_ptr<BlurredBoxTextureProvider> textureProvider) : m_textureProvider(std::move(textureProvider))
{ {
setNodeRoles( { PanelRole } ); setNodeRoles( { PanelRole } );
} }
BlurredBoxSkinlet::~BlurredBoxSkinlet()
{
m_textureProvider = nullptr;
}
QRectF BlurredBoxSkinlet::subControlRect( QRectF BlurredBoxSkinlet::subControlRect(
const QskSkinnable*, const QRectF& contentsRect, QskAspect::Subcontrol ) const const QskSkinnable*, const QRectF& contentsRect, QskAspect::Subcontrol ) const
{ {
@ -30,12 +41,11 @@ QSGNode* BlurredBoxSkinlet::updateSubNode(
{ {
case PanelRole: case PanelRole:
auto* const blurred = QskSGNode::ensureNode< BlurredBoxNode >( node ); auto* const blurred = QskSGNode::ensureNode< BlurredBoxNode >( node );
const auto rectOfScreen = box->rectOfScreen();
const auto rectOnScreen = box->rectOnScreen();
const auto boxShapeHint = box->boxShapeHint(BlurredBox::Panel); 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(), static_cast< float >( box->opacity() ), box->blurDirections(), box->blurQuality(),
box->blurSize() ); box->blurSize(), m_textureProvider.get());
return blurred; return blurred;
} }

View File

@ -1,9 +1,10 @@
#pragma once #pragma once
#include <QskSGNode.h>
#include <QskSkinlet.h> #include <QskSkinlet.h>
#include <memory>
#include "BlurredBoxTextureProvider.h"
class BlurredBoxSkinlet : public QskSkinlet class BlurredBoxSkinlet final : public QskSkinlet
{ {
public: public:
enum NodeRole enum NodeRole
@ -11,9 +12,12 @@ class BlurredBoxSkinlet : public QskSkinlet
PanelRole PanelRole
}; };
BlurredBoxSkinlet(); BlurredBoxSkinlet(std::shared_ptr<BlurredBoxTextureProvider> textureProvider);
~BlurredBoxSkinlet() override;
QRectF subControlRect( QRectF subControlRect(
const QskSkinnable*, const QRectF& contentsRect, QskAspect::Subcontrol ) const override; const QskSkinnable*, const QRectF& contentsRect, QskAspect::Subcontrol ) const override;
QSGNode* updateSubNode( QSGNode* updateSubNode(
const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const override; const QskSkinnable* skinnable, quint8 nodeRole, QSGNode* node ) const override;
private:
std::shared_ptr<BlurredBoxTextureProvider> m_textureProvider;
}; };

View File

@ -0,0 +1,32 @@
#include "BlurredBoxTextureProvider.h"
#include <QQuickWindow>
#include <QSGTexture>
#include <memory>
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);
}

View File

@ -0,0 +1,20 @@
#pragma once
#include <QSGTextureProvider>
#include <QImage>
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;
};

View File

@ -1,5 +1,7 @@
#include "MainWindow.h" #include "MainWindow.h"
#include "BlurredBox.h" #include "BlurredBox.h"
#include "BlurredBoxTextureProvider.h"
#include "BlurredBoxSkinlet.h"
#include <QColor> #include <QColor>
#include <QskLinearBox.h> #include <QskLinearBox.h>
@ -7,64 +9,70 @@
#include <QskGraphic.h> #include <QskGraphic.h>
#include <QskGraphicLabel.h> #include <QskGraphicLabel.h>
#include <QskGridBox.h>
#include <QskSegmentedBar.h> #include <QskSegmentedBar.h>
#include <QskShortcutMap.h> #include <QskShortcutMap.h>
#include <QskSkin.h> #include <QskSkin.h>
#include <QskSlider.h> #include <QskSlider.h>
#include <QskTextInput.h> #include <QskTextInput.h>
#include <QskTextLabel.h> #include <QskTextLabel.h>
#include <QQuickItemGrabResult>
#include <QSGTextureProvider>
#include <QskAnimator.h>
#include <QskBoxShapeMetrics.h>
#include <QskPushButton.h>
MainWindow::MainWindow() #include <memory>
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 ); setMinimumSize( size );
setMaximumSize( size ); setMaximumSize( size );
setTitle( tr( "Blurring" ) ); setTitle( tr( "Blurring" ) );
m_stack->setAutoLayoutChildren(true);
createBackground(); createBackground();
// create a centered, blurred and rounded rectangle QskShortcutMap::addShortcut( Qt::CTRL | Qt::Key_O, false, contentItem(), [this](){
auto* const layout = new QskLinearBox( Qt::Vertical, contentItem() ); createOverlay();
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;
} }
void MainWindow::createBackground() void MainWindow::createBackground()
{ {
// create a brackground image // 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->setFillMode( QskGraphicLabel::FillMode::Stretch );
graphic->setAlignment( Qt::AlignCenter ); graphic->setAlignment( Qt::AlignCenter );
// callback for rotating through the background images static int index = 0;
auto updateBackground = [ this, graphic ]() { const QImage image( QString( ":/backgrounds/background%1.jpg" ).arg( 1 + index++ ) );
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 ) ); graphic->setGraphic( QskGraphic::fromImage( image ) );
m_stack->addItem(graphic);
update(); update();
};
updateBackground();
QKeySequence keys( Qt::CTRL | Qt::Key_Space );
QskShortcutMap::addShortcut( keys, false, contentItem(), updateBackground );
} }
void MainWindow::createBlurDirectionsControls( BlurredBox* blurred, QskLinearBox* layout ) void MainWindow::createBlurDirectionsControls( BlurredBox* blurred, QskLinearBox* layout )
@ -103,7 +111,7 @@ void MainWindow::createBlurSizeControls( BlurredBox* blurred, QskLinearBox* layo
label->setTextColor( Qt::white ); label->setTextColor( Qt::white );
label->setFontRole( QskSkin::MediumFont ); label->setFontRole( QskSkin::MediumFont );
auto* const slider = new QskSlider( Qt::Horizontal, layout ); auto* const slider = new QskSlider( Qt::Horizontal, layout );
slider->setMinimum( 4.0 ); slider->setMinimum( 0.0 );
slider->setMaximum( 32.0 ); slider->setMaximum( 32.0 );
connect( slider, &QskSlider::valueChanged, slider, [ blurred, label ]( qreal value ) { connect( slider, &QskSlider::valueChanged, slider, [ blurred, label ]( qreal value ) {
blurred->setBlurSize( static_cast< float >( value ) ); blurred->setBlurSize( static_cast< float >( value ) );
@ -203,4 +211,43 @@ void MainWindow::createShortcutNote( QskLinearBox* layout )
label->setFontRole( QskSkin::LargeFont ); label->setFontRole( QskSkin::LargeFont );
} }
void MainWindow::createOverlay()
{
auto* const skinlet = new BlurredBoxSkinlet(std::make_shared<BlurredBoxTextureProvider>(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" #include "moc_MainWindow.cpp"

View File

@ -1,6 +1,7 @@
#pragma once #pragma once
#include <QskWindow.h> #include <QskWindow.h>
#include <QskStackBox.h>
class BlurredBox; class BlurredBox;
class QskLinearBox; class QskLinearBox;
@ -20,4 +21,7 @@ class MainWindow : public QskWindow
void createBlurOpacityControls( BlurredBox* blurred, QskLinearBox* layout ); void createBlurOpacityControls( BlurredBox* blurred, QskLinearBox* layout );
void createBlurCornerRadiiControls( BlurredBox* blurred, QskLinearBox* layout ); void createBlurCornerRadiiControls( BlurredBox* blurred, QskLinearBox* layout );
void createShortcutNote( QskLinearBox* layout ); void createShortcutNote( QskLinearBox* layout );
void createOverlay();
QskStackBox* m_stack = nullptr;
}; };

View File

@ -1,6 +1,7 @@
CONFIG += qskexample 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 += \ SOURCES += \
main.cpp \ main.cpp \
@ -10,6 +11,7 @@ SOURCES += \
BlurredBoxMaterialShader.cpp \ BlurredBoxMaterialShader.cpp \
BlurredBoxNode.cpp \ BlurredBoxNode.cpp \
BlurredBoxSkinlet.cpp \ BlurredBoxSkinlet.cpp \
BlurredBoxTextureProvider.cpp
HEADERS += \ HEADERS += \
MainWindow.h \ MainWindow.h \
@ -18,6 +20,7 @@ HEADERS += \
BlurredBoxMaterialShader.h \ BlurredBoxMaterialShader.h \
BlurredBoxNode.h \ BlurredBoxNode.h \
BlurredBoxSkinlet.h \ BlurredBoxSkinlet.h \
BlurredBoxTextureProvider.h
RESOURCES += \ RESOURCES += \
blurringshaders.qrc blurringshaders.qrc

View File

@ -2,53 +2,37 @@
uniform lowp float rectOpacity; uniform lowp float rectOpacity;
// x = top left, y = top right, z = bottom right, w = bottom left // x = top left, y = top right, z = bottom right, w = bottom left
uniform lowp vec4 rectCornerRadii; uniform lowp vec4 rectCornerRadii;
// the rectangle on the screen (x, y, width, height) // the rectangle's aspect ratio
uniform lowp vec4 rectOnScreen; uniform lowp vec2 rectAspect;
// the screen's rectangle (x, y, width, height)
uniform lowp vec4 rectOfScreen;
// must be greater than 0.0! // must be greater than 0.0!
uniform lowp float blurDirections; uniform lowp float blurDirections;
// must be greater than 0.0! // must be greater than 0.0!
uniform lowp float blurQuality; uniform lowp float blurQuality;
// the radius for blurring // the radius for blurring
uniform lowp float blurSize; uniform lowp vec2 blurRadius;
// normalized position of the fragment // normalized position of the fragment
varying lowp vec2 coord; 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 sampler2D txr;
uniform lowp float edgeSoftness;
lowp float effectiveRadius( in lowp vec4 radii, in lowp vec2 point ) lowp float effectiveRadius( in lowp vec4 radii, in lowp vec2 point )
{ {
// aliases if ( point.y < 0.5 )
lowp float px = point.x * rectOfScreen.z; return ( point.x < 0.5) ? radii.x : radii.y;
lowp float py = point.y * rectOfScreen.w; else
lowp float rx = rectOnScreen.x; return ( point.x < 0.5) ? radii.z : radii.w;
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(lowp vec2 centerPosition, lowp vec2 size, lowp float radius)
lowp float roundedBoxSDF(vec2 centerPosition, vec2 size, lowp float radius)
{ {
return length( max( abs(centerPosition) - size + radius, 0.0) ) - radius; return length( max( abs(centerPosition) - size + radius, 0.0) ) - radius;
} }
// source: https://www.shadertoy.com/view/Xltfzj
void main() void main()
{ {
if(rectOpacity == 0.0) if(rectOpacity == 0.0)
@ -56,29 +40,39 @@ void main()
return; return;
} }
// Radius for blurring around the pixel lowp vec2 rectAspectHalf = rectAspect / 2.0;
vec2 blurRadius = blurSize / rectOnScreen.zw;
// Pixel colour // Pixel color
vec4 fragColor = texture2D(txr, coord); lowp vec4 fragColorOrig = texture2D(txr, coord);
lowp vec4 fragColor = fragColorOrig;
// Blur calculations // Blur calculations
const lowp float Pi = 6.28318530718; // constant for Pi*2 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 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); // Calculate distance to edge.
lowp float smoothedAlpha = 1.0f - smoothstep(0.0f, edgeSoftness * 2.0f, distance); 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 // Output to screen
fragColor /= blurQuality * blurDirections - 15.0; 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