QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskBoxShadowNode.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskBoxShadowNode.h"
7#include "QskBoxShapeMetrics.h"
8
9#include <qcolor.h>
10#include <qsgmaterialshader.h>
11#include <qsgmaterial.h>
12
13QSK_QT_PRIVATE_BEGIN
14#include <private/qsgnode_p.h>
15QSK_QT_PRIVATE_END
16
17// QSGMaterialRhiShader became QSGMaterialShader in Qt6
18
19#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
20 #include <QSGMaterialRhiShader>
21 using RhiShader = QSGMaterialRhiShader;
22#else
23 using RhiShader = QSGMaterialShader;
24#endif
25
26namespace
27{
28 class Material final : public QSGMaterial
29 {
30 public:
31 Material();
32
33#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
34 QSGMaterialShader* createShader() const override;
35#else
36 QSGMaterialShader* createShader( QSGRendererInterface::RenderMode ) const override;
37#endif
38
39 QSGMaterialType* type() const override;
40
41 int compare( const QSGMaterial* other ) const override;
42
43 QVector2D m_aspectRatio = QVector2D{ 1, 1 };
44 QVector4D m_radius = QVector4D{ 0, 0, 0, 0 };
45 QVector4D m_color = QVector4D{ 0, 0, 0, 1 };
46 float m_blurExtent = 0.0;
47 };
48}
49
50namespace
51{
52 class ShaderRhi final : public RhiShader
53 {
54 public:
55 ShaderRhi()
56 {
57 const QString root( ":/qskinny/shaders/" );
58
59 setShaderFileName( VertexStage, root + "boxshadow.vert.qsb" );
60 setShaderFileName( FragmentStage, root + "boxshadow.frag.qsb" );
61 }
62
63 bool updateUniformData( RenderState& state,
64 QSGMaterial* newMaterial, QSGMaterial* oldMaterial ) override
65 {
66 const auto matOld = static_cast< Material* >( oldMaterial );
67 const auto matNew = static_cast< Material* >( newMaterial );
68
69 Q_ASSERT( state.uniformData()->size() >= 112 );
70
71 auto data = state.uniformData()->data();
72 bool changed = false;
73
74 if ( state.isMatrixDirty() )
75 {
76 const auto matrix = state.combinedMatrix();
77 memcpy( data + 0, matrix.constData(), 64 );
78
79 changed = true;
80 }
81
82 if ( matOld == nullptr || matNew->m_color != matOld->m_color )
83 {
84 memcpy( data + 64, &matNew->m_color, 16 );
85 changed = true;
86 }
87
88 if ( matOld == nullptr || matNew->m_radius != matOld->m_radius )
89 {
90 memcpy( data + 80, &matNew->m_radius, 16 );
91 changed = true;
92 }
93
94 if ( matOld == nullptr || matNew->m_aspectRatio != matOld->m_aspectRatio )
95 {
96 memcpy( data + 96, &matNew->m_aspectRatio, 8 );
97 changed = true;
98 }
99
100 if ( matOld == nullptr || matNew->m_blurExtent != matOld->m_blurExtent )
101 {
102 memcpy( data + 104, &matNew->m_blurExtent, 4 );
103 changed = true;
104 }
105
106 if ( state.isOpacityDirty() )
107 {
108 const float opacity = state.opacity();
109 memcpy( data + 108, &opacity, 4 );
110
111 changed = true;
112 }
113
114 return changed;
115 }
116 };
117}
118
119#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
120
121namespace
122{
123 // the old type of shader - spcific for OpenGL
124
125 class ShaderGL final : public QSGMaterialShader
126 {
127 public:
128 ShaderGL()
129 {
130 const QString root( ":/qskinny/shaders/" );
131
132 setShaderSourceFile( QOpenGLShader::Vertex, root + "boxshadow.vert" );
133 setShaderSourceFile( QOpenGLShader::Fragment, root + "boxshadow.frag" );
134 }
135
136 char const* const* attributeNames() const override
137 {
138 static char const* const names[] = { "in_vertex", "in_coord", nullptr };
139 return names;
140 }
141
142 void initialize() override
143 {
144 QSGMaterialShader::initialize();
145
146 auto p = program();
147
148 m_matrixId = p->uniformLocation( "matrix" );
149 m_aspectRatioId = p->uniformLocation( "aspectRatio" );
150 m_opacityId = p->uniformLocation( "opacity" );
151 m_blurExtentId = p->uniformLocation( "blurExtent" );
152 m_radiusId = p->uniformLocation( "radius" );
153 m_colorId = p->uniformLocation( "color" );
154 }
155
156 void updateState( const QSGMaterialShader::RenderState& state,
157 QSGMaterial* newMaterial, QSGMaterial* oldMaterial) override
158 {
159 auto p = program();
160
161 if ( state.isMatrixDirty() )
162 p->setUniformValue( m_matrixId, state.combinedMatrix() );
163
164 if ( state.isOpacityDirty() )
165 p->setUniformValue( m_opacityId, state.opacity() );
166
167 bool updateMaterial = ( oldMaterial == nullptr )
168 || newMaterial->compare( oldMaterial ) != 0;
169
170 updateMaterial |= state.isCachedMaterialDataDirty();
171
172 if ( updateMaterial )
173 {
174 auto material = static_cast< const Material* >( newMaterial );
175
176 p->setUniformValue( m_aspectRatioId, material->m_aspectRatio );
177 p->setUniformValue( m_blurExtentId, material->m_blurExtent);
178 p->setUniformValue( m_radiusId, material->m_radius );
179 p->setUniformValue( m_colorId, material->m_color );
180 }
181 }
182
183 private:
184 int m_matrixId = -1;
185 int m_opacityId = -1;
186 int m_aspectRatioId = -1;
187 int m_blurExtentId = -1;
188 int m_radiusId = -1;
189 int m_colorId = -1;
190 };
191}
192
193#endif
194
195Material::Material()
196{
197 setFlag( QSGMaterial::Blending, true );
198
199#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
200 setFlag( QSGMaterial::SupportsRhiShader, true );
201#endif
202}
203
204#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
205
206QSGMaterialShader* Material::createShader() const
207{
208 if ( !( flags() & QSGMaterial::RhiShaderWanted ) )
209 return new ShaderGL();
210
211 return new ShaderRhi();
212}
213
214#else
215
216QSGMaterialShader* Material::createShader( QSGRendererInterface::RenderMode ) const
217{
218 return new ShaderRhi();
219}
220
221#endif
222
223QSGMaterialType* Material::type() const
224{
225 static QSGMaterialType staticType;
226 return &staticType;
227}
228
229int Material::compare( const QSGMaterial* other ) const
230{
231 auto material = static_cast< const Material* >( other );
232
233 if ( ( material->m_color == m_color )
234 && ( material->m_aspectRatio == m_aspectRatio )
235 && qFuzzyCompare(material->m_blurExtent, m_blurExtent)
236 && qFuzzyCompare(material->m_radius, m_radius) )
237 {
238 return 0;
239 }
240
241 return QSGMaterial::compare( other );
242}
243
244class QskBoxShadowNodePrivate final : public QSGGeometryNodePrivate
245{
246 public:
247 QskBoxShadowNodePrivate()
248 : geometry( QSGGeometry::defaultAttributes_TexturedPoint2D(), 4 )
249 {
250 }
251
252 QSGGeometry geometry;
253 Material material;
254
255 QRectF rect;
256};
257
258QskBoxShadowNode::QskBoxShadowNode()
259 : QSGGeometryNode( *new QskBoxShadowNodePrivate )
260{
261 Q_D( QskBoxShadowNode );
262
263 setGeometry( &d->geometry );
264 setMaterial( &d->material );
265}
266
267QskBoxShadowNode::~QskBoxShadowNode()
268{
269}
270
271void QskBoxShadowNode::setShadowData(
272 const QRectF& rect, const QskBoxShapeMetrics& shapeMetrics,
273 qreal blurRadius, const QColor& color )
274{
275 Q_D( QskBoxShadowNode );
276
277 if ( rect != d->rect )
278 {
279 d->rect = rect;
280
281 QSGGeometry::updateTexturedRectGeometry(
282 &d->geometry, d->rect, QRectF( -0.5, -0.5, 1.0, 1.0 ) );
283
284 d->geometry.markVertexDataDirty();
285 markDirty( QSGNode::DirtyGeometry );
286
287 QVector2D aspectRatio( 1.0, 1.0 );
288
289 if ( rect.width() >= rect.height() )
290 aspectRatio.setX( rect.width() / rect.height() );
291 else
292 aspectRatio.setY( rect.height() / rect.width() );
293
294 if ( d->material.m_aspectRatio != aspectRatio )
295 {
296 d->material.m_aspectRatio = aspectRatio;
297 markDirty( QSGNode::DirtyMaterial );
298 }
299 }
300
301 {
302 const auto shape = shapeMetrics.toAbsolute( rect.size() );
303
304 const float t = std::min( d->rect.width(), d->rect.height() );
305
306 const float r1 = shape.radius( Qt::BottomRightCorner ).width();
307 const float r2 = shape.radius( Qt::TopRightCorner ).width();
308 const float r3 = shape.radius( Qt::BottomLeftCorner ).width();
309 const float r4 = shape.radius( Qt::TopLeftCorner ).width();
310
311 const auto uniformRadius = QVector4D(
312 std::min( r1 / t, 1.0f ), std::min( r2 / t, 1.0f ),
313 std::min( r3 / t, 1.0f ), std::min( r4 / t, 1.0f ) );
314
315 if ( d->material.m_radius != uniformRadius )
316 {
317 d->material.m_radius = uniformRadius;
318 markDirty( QSGNode::DirtyMaterial );
319 }
320 }
321
322 {
323 if ( blurRadius <= 0.0 )
324 blurRadius = 0.0;
325
326 const float t = 0.5 * std::min( d->rect.width(), d->rect.height() );
327 const float uniformExtent = blurRadius / t;
328
329 if ( !qFuzzyCompare( d->material.m_blurExtent, uniformExtent ) )
330 {
331 d->material.m_blurExtent = uniformExtent;
332 markDirty( QSGNode::DirtyMaterial );
333 }
334 }
335
336 {
337 const auto a = color.alphaF();
338
339 const QVector4D c( color.redF() * a, color.greenF() * a, color.blueF() * a, a );
340
341 if ( d->material.m_color != c )
342 {
343 d->material.m_color = c;
344 markDirty( QSGNode::DirtyMaterial );
345 }
346 }
347}