6#include "QskGradientMaterial.h"
7#include "QskFunctions.h"
8#include "QskRgbValue.h"
9#include "QskGradientDirection.h"
10#include "QskColorRamp.h"
12#include <qsgtexture.h>
19#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
24#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
25 #include <qsgmaterialrhishader.h>
26 using RhiShader = QSGMaterialRhiShader;
28 using RhiShader = QSGMaterialShader;
36 GradientMaterial( QskGradient::Type type )
39 setFlag( Blending | RequiresFullMatrix );
41#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
42 setFlag( QSGMaterial::SupportsRhiShader,
true );
46 int compare(
const QSGMaterial* other )
const override
48 const auto mat =
static_cast< const GradientMaterial*
>( other );
50 if ( ( spreadMode() == mat->spreadMode() ) && ( stops() == mat->stops() ) )
53 return QSGMaterial::compare( other );
56#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
59 QSGMaterialShader* createShader(
60 QSGRendererInterface::RenderMode )
const override final
62 return createShader();
65 virtual QSGMaterialShader* createShader()
const = 0;
73 class GradientShaderGL :
public QSGMaterialShader
76 void setShaderFiles(
const char* name )
78 static const QString root(
":/qskinny/shaders/" );
80 setShaderSourceFile( QOpenGLShader::Vertex, root + name +
".vert" );
81 setShaderSourceFile( QOpenGLShader::Fragment, root + name +
".frag" );
84 void initialize()
override
86 m_opacityId = program()->uniformLocation(
"opacity" );
87 m_matrixId = program()->uniformLocation(
"matrix" );
90 void updateState(
const RenderState& state,
91 QSGMaterial* newMaterial, QSGMaterial* )
override final
94 auto material =
static_cast< GradientMaterial*
>( newMaterial );
96 if ( state.isOpacityDirty() )
97 p->setUniformValue( m_opacityId, state.opacity() );
99 if ( state.isMatrixDirty() )
100 p->setUniformValue(m_matrixId, state.combinedMatrix() );
102 updateUniformValues( material );
104 auto texture = QskColorRamp::texture(
105 nullptr, material->stops(), material->spreadMode() );
109 char const*
const* attributeNames() const override final
111 static const char*
const attr[] = {
"vertexCoord",
nullptr };
115 virtual void updateUniformValues(
const GradientMaterial* ) = 0;
118 int m_opacityId = -1;
124 class GradientShaderRhi :
public RhiShader
127 void setShaderFiles(
const char* name )
129 static const QString root(
":/qskinny/shaders/" );
131 setShaderFileName( VertexStage, root + name +
".vert.qsb" );
132 setShaderFileName( FragmentStage, root + name +
".frag.qsb" );
135 void updateSampledImage( RenderState& state,
int binding,
136 QSGTexture* textures[], QSGMaterial* newMaterial, QSGMaterial* )
override final
141 auto material =
static_cast< const GradientMaterial*
>( newMaterial );
143 auto texture = QskColorRamp::texture(
144 state.rhi(), material->stops(), material->spreadMode() );
146#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
147 texture->updateRhiTexture( state.rhi(), state.resourceUpdateBatch() );
149 texture->commitTextureOperations( state.rhi(), state.resourceUpdateBatch() );
152 textures[0] = texture;
160 class LinearMaterial final :
public GradientMaterial
168 bool setGradient(
const QskGradient& gradient )
override
170 bool changed =
false;
172 if ( gradient.stops() != stops() )
174 setStops( gradient.stops() );
184 if ( gradient.spreadMode() != spreadMode() )
186 setSpreadMode( gradient.spreadMode() );
190 const auto dir = gradient.linearDirection();
192 const QVector4D vector( dir.x1(), dir.y1(),
193 dir.x2() - dir.x1(), dir.y2() - dir.y1() );
195 if ( m_gradientVector != vector )
197 m_gradientVector = vector;
204 QSGMaterialType* type()
const override
206 static QSGMaterialType type;
210 int compare(
const QSGMaterial* other )
const override
212 const auto mat =
static_cast< const LinearMaterial*
>( other );
214 if ( m_gradientVector != mat->m_gradientVector )
215 return QSGMaterial::compare( other );
217 return GradientMaterial::compare( other );
220 QSGMaterialShader* createShader()
const override;
226 QVector4D m_gradientVector;
230 class LinearShaderGL final :
public GradientShaderGL
235 setShaderFiles(
"gradientlinear" );
238 void initialize()
override
240 GradientShaderGL::initialize();
241 m_vectorId = program()->uniformLocation(
"vector" );
244 void updateUniformValues(
const GradientMaterial* newMaterial )
override
246 auto material =
static_cast< const LinearMaterial*
>( newMaterial );
247 program()->setUniformValue( m_vectorId, material->m_gradientVector );
256 class LinearShaderRhi final :
public GradientShaderRhi
261 setShaderFiles(
"gradientlinear" );
264 bool updateUniformData( RenderState& state,
265 QSGMaterial* newMaterial, QSGMaterial* oldMaterial )
override
267 auto matNew =
static_cast< LinearMaterial*
>( newMaterial );
268 auto matOld =
static_cast< LinearMaterial*
>( oldMaterial );
270 Q_ASSERT( state.uniformData()->size() >= 84 );
272 auto data = state.uniformData()->data();
273 bool changed =
false;
275 if ( state.isMatrixDirty() )
277 const auto matrix = state.combinedMatrix();
278 memcpy( data + 0, matrix.constData(), 64 );
283 if ( matOld ==
nullptr || matNew->m_gradientVector != matOld->m_gradientVector )
285 memcpy( data + 64, &matNew->m_gradientVector, 16 );
289 if ( state.isOpacityDirty() )
291 const float opacity = state.opacity();
292 memcpy( data + 80, &opacity, 4 );
302 QSGMaterialShader* LinearMaterial::createShader()
const
304#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
305 if ( !( flags() & QSGMaterial::RhiShaderWanted ) )
306 return new LinearShaderGL;
308 return new LinearShaderRhi;
314 class RadialMaterial final :
public GradientMaterial
322 QSGMaterialType* type()
const override
324 static QSGMaterialType type;
328 bool setGradient(
const QskGradient& gradient )
override
330 bool changed =
false;
332 if ( gradient.stops() != stops() )
334 setStops( gradient.stops() );
338 if ( gradient.spreadMode() != spreadMode() )
340 setSpreadMode( gradient.spreadMode() );
344 const auto dir = gradient.radialDirection();
346 const QVector2D pos( dir.x(), dir.y() );
347 const QVector2D radius( dir.radiusX(), dir.radiusY() );
349 if ( ( pos != m_center ) || ( m_radius != radius ) )
360 int compare(
const QSGMaterial* other )
const override
362 const auto mat =
static_cast< const RadialMaterial*
>( other );
364 if ( ( m_center != mat->m_center ) || ( m_radius != mat->m_radius ) )
366 return QSGMaterial::compare( other );
370 return GradientMaterial::compare( other );
374 QSGMaterialShader* createShader()
const override;
381 class RadialShaderGL final :
public GradientShaderGL
386 setShaderFiles(
"gradientradial" );
389 void initialize()
override
391 GradientShaderGL::initialize();
395 m_centerCoordId = p->uniformLocation(
"centerCoord" );
396 m_radiusId = p->uniformLocation(
"radius" );
399 void updateUniformValues(
const GradientMaterial* newMaterial )
override
401 auto material =
static_cast< const RadialMaterial*
>( newMaterial );
405 p->setUniformValue( m_centerCoordId, material->m_center );
406 p->setUniformValue( m_radiusId, material->m_radius );
410 int m_centerCoordId = -1;
416 class RadialShaderRhi final :
public GradientShaderRhi
421 setShaderFiles(
"gradientradial" );
424 bool updateUniformData( RenderState& state,
425 QSGMaterial* newMaterial, QSGMaterial* oldMaterial)
override
427 auto matNew =
static_cast< RadialMaterial*
>( newMaterial );
428 auto matOld =
static_cast< RadialMaterial*
>( oldMaterial );
430 Q_ASSERT( state.uniformData()->size() >= 84 );
432 auto data = state.uniformData()->data();
433 bool changed =
false;
435 if ( state.isMatrixDirty() )
437 const auto matrix = state.combinedMatrix();
438 memcpy( data + 0, matrix.constData(), 64 );
443 if ( matOld ==
nullptr || matNew->m_center != matOld->m_center )
445 memcpy( data + 64, &matNew->m_center, 8 );
449 if ( matOld ==
nullptr || matNew->m_radius != matOld->m_radius )
451 memcpy( data + 72, &matNew->m_radius, 8 );
455 if ( state.isOpacityDirty() )
457 const float opacity = state.opacity();
458 memcpy( data + 80, &opacity, 4 );
468 QSGMaterialShader* RadialMaterial::createShader()
const
471 if ( !( flags() & QSGMaterial::RhiShaderWanted ) )
472 return new RadialShaderGL;
475 return new RadialShaderRhi;
481 class ConicMaterial final :
public GradientMaterial
489 QSGMaterialType* type()
const override
491 static QSGMaterialType type;
495 bool setGradient(
const QskGradient& gradient )
override
497 bool changed =
false;
499 if ( gradient.stops() != stops() )
501 setStops( gradient.stops() );
505 if ( gradient.spreadMode() != spreadMode() )
507 setSpreadMode( gradient.spreadMode() );
511 const auto dir = gradient.conicDirection();
513 float ratio = dir.aspectRatio();
517 if ( ratio != m_aspectRatio )
519 m_aspectRatio = ratio;
523 const QVector2D center( dir.x(), dir.y() );
525 if ( center != m_center )
533 float start = fmod( dir.startAngle(), 360.0 ) / 360.0;
539 if ( dir.spanAngle() >= 360.0 )
543 else if ( dir.spanAngle() <= -360.0 )
549 span = fmod( dir.spanAngle(), 360.0 ) / 360.0;
552 if ( ( start != m_start ) || ( span != m_span ) )
563 int compare(
const QSGMaterial* other )
const override
565 const auto mat =
static_cast< const ConicMaterial*
>( other );
567 if ( ( m_center != mat->m_center )
568 || !qskFuzzyCompare( m_aspectRatio, mat->m_aspectRatio )
569 || !qskFuzzyCompare( m_start, mat->m_start )
570 || !qskFuzzyCompare( m_span, mat->m_span ) )
572 return QSGMaterial::compare( other );
575 return GradientMaterial::compare( other );
578 QSGMaterialShader* createShader()
const override;
581 float m_aspectRatio = 1.0;
587 class ConicShaderGL final :
public GradientShaderGL
592 setShaderFiles(
"gradientconic" );
595 void initialize()
override
597 GradientShaderGL::initialize();
599 m_centerCoordId = program()->uniformLocation(
"centerCoord" );
600 m_aspectRatioId = program()->uniformLocation(
"aspectRatio" );
601 m_startId = program()->uniformLocation(
"start" );
602 m_spanId = program()->uniformLocation(
"span" );
605 void updateUniformValues(
const GradientMaterial* newMaterial )
override
607 auto material =
static_cast< const ConicMaterial*
>( newMaterial );
609 program()->setUniformValue( m_centerCoordId, material->m_center );
610 program()->setUniformValue( m_aspectRatioId, material->m_aspectRatio );
611 program()->setUniformValue( m_startId, material->m_start );
612 program()->setUniformValue( m_spanId, material->m_span );
616 int m_centerCoordId = -1;
617 int m_aspectRatioId = -1;
624 class ConicShaderRhi final :
public GradientShaderRhi
629 setShaderFiles(
"gradientconic" );
632 bool updateUniformData( RenderState& state,
633 QSGMaterial* newMaterial, QSGMaterial* oldMaterial )
override
635 auto matNew =
static_cast< ConicMaterial*
>( newMaterial );
636 auto matOld =
static_cast< ConicMaterial*
>( oldMaterial );
638 Q_ASSERT( state.uniformData()->size() >= 88 );
640 auto data = state.uniformData()->data();
641 bool changed =
false;
643 if ( state.isMatrixDirty() )
645 const auto matrix = state.combinedMatrix();
646 memcpy( data + 0, matrix.constData(), 64 );
651 if ( matOld ==
nullptr || matNew->m_center != matOld->m_center )
653 memcpy( data + 64, &matNew->m_center, 8 );
657 if ( matOld ==
nullptr || matNew->m_aspectRatio != matOld->m_aspectRatio )
659 memcpy( data + 72, &matNew->m_aspectRatio, 4 );
663 if ( matOld ==
nullptr || matNew->m_start != matOld->m_start )
665 memcpy( data + 76, &matNew->m_start, 4 );
669 if ( matOld ==
nullptr || matNew->m_span != matOld->m_span )
671 memcpy( data + 80, &matNew->m_span, 4 );
675 if ( state.isOpacityDirty() )
677 const float opacity = state.opacity();
678 memcpy( data + 84, &opacity, 4 );
688 QSGMaterialShader* ConicMaterial::createShader()
const
691 if ( !( flags() & QSGMaterial::RhiShaderWanted ) )
692 return new ConicShaderGL;
694 return new ConicShaderRhi;
698QskGradientMaterial::QskGradientMaterial( QskGradient::Type type )
699 : m_gradientType( type )
703template<
typename Material >
706 if ( material ==
nullptr )
707 material =
new Material();
709 return static_cast< Material*
>( material );
712bool QskGradientMaterial::updateGradient(
const QRectF& rect,
const QskGradient& gradient )
714 Q_ASSERT( gradient.type() == m_gradientType );
716 if ( gradient.type() == m_gradientType )
718 switch ( gradient.type() )
720 case QskGradient::Linear:
721 case QskGradient::Radial:
722 case QskGradient::Conic:
724 auto material =
static_cast< GradientMaterial*
>( this );
725 return material->setGradient( gradient.stretchedTo( rect ) );
729 qWarning(
"Invalid gradient type" );
738 switch ( gradientType )
740 case QskGradient::Linear:
741 return new LinearMaterial();
743 case QskGradient::Radial:
744 return new RadialMaterial();
746 case QskGradient::Conic:
747 return new ConicMaterial();