diff --git a/playground/shapes/ShapeItem.cpp b/playground/shapes/ShapeItem.cpp index 01d46379..3e100e45 100644 --- a/playground/shapes/ShapeItem.cpp +++ b/playground/shapes/ShapeItem.cpp @@ -33,20 +33,17 @@ QPen ShapeItem::pen() const return m_pen; } -void ShapeItem::setFillColor( const QColor& color ) +void ShapeItem::setGradient( const QColor& c1, const QColor& c2 ) { - if ( color != m_fillColor ) + if ( c1 != m_fillColor[0] || c2 != m_fillColor[1] ) { - m_fillColor = color; + m_fillColor[0] = c1; + m_fillColor[1] = c2; + update(); } } -QColor ShapeItem::fillColor() const -{ - return m_fillColor; -} - void ShapeItem::setPath( const QPainterPath& path ) { if ( path != m_path ) @@ -92,7 +89,20 @@ void ShapeItem::updateNode( QSGNode* parentNode ) QskSGNode::setNodeRole( fillNode, FillRole ); } - fillNode->updateNode( path, m_fillColor ); + if ( m_fillColor[0] != m_fillColor[1] ) + { + QLinearGradient gradient; + gradient.setStart( rect.topLeft() ); + gradient.setFinalStop( rect.bottomRight() ); + gradient.setColorAt( 0.0, m_fillColor[0] ); + gradient.setColorAt( 1.0, m_fillColor[1] ); + + fillNode->updateNode( path, &gradient ); + } + else + { + fillNode->updateNode( path, m_fillColor[0] ); + } if ( fillNode->parent() != parentNode ) parentNode->prependChildNode( fillNode ); diff --git a/playground/shapes/ShapeItem.h b/playground/shapes/ShapeItem.h index c77eb455..a2dbb63d 100644 --- a/playground/shapes/ShapeItem.h +++ b/playground/shapes/ShapeItem.h @@ -20,8 +20,7 @@ class ShapeItem : public QskControl void setPen( const QPen& ); QPen pen() const; - void setFillColor( const QColor& ); - QColor fillColor() const; + void setGradient( const QColor&, const QColor& ); void setPath( const QPainterPath& ); QPainterPath path() const; @@ -33,6 +32,6 @@ class ShapeItem : public QskControl QPainterPath scaledPath( const QRectF& ) const; QPen m_pen; - QColor m_fillColor; + QColor m_fillColor[2]; QPainterPath m_path; }; diff --git a/playground/shapes/main.cpp b/playground/shapes/main.cpp index bd95405e..8293e3b9 100644 --- a/playground/shapes/main.cpp +++ b/playground/shapes/main.cpp @@ -36,8 +36,8 @@ int main( int argc, char* argv[] ) auto shapeItem = new ShapeItem(); shapeItem->setPath( path( SkinnyShapeFactory::Hexagon ) ); - shapeItem->setPen( QPen( Qt::darkBlue, 20 ) ); - shapeItem->setFillColor( Qt::darkYellow ); + shapeItem->setPen( QPen( Qt::darkCyan, 20 ) ); + shapeItem->setGradient( Qt::red, Qt::blue ); window.addItem( shapeItem ); window.resize( 600, 600 ); diff --git a/src/nodes/QskGradientMaterial.cpp b/src/nodes/QskGradientMaterial.cpp new file mode 100644 index 00000000..cbc510ab --- /dev/null +++ b/src/nodes/QskGradientMaterial.cpp @@ -0,0 +1,812 @@ +#include "QskGradientMaterial.h" +#include "QskFunctions.h" +#include "QskRgbValue.h" + +QSK_QT_PRIVATE_BEGIN +#include +#include +#include +QSK_QT_PRIVATE_END + +// RHI shaders are supported by Qt 5.15 and Qt 6.x +#define SHADER_RHI + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + // Old type of shaders only with Qt 5.x + #define SHADER_GL +#endif + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + #include + using RhiShader = QSGMaterialRhiShader; +#else + using RhiShader = QSGMaterialShader; +#endif + +/* + The shaders for the gradient are implemented in the quickshapes module + Beside them we do not need this module. + */ + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + + QSK_QT_PRIVATE_BEGIN + #ifndef signals + #define signals Q_SIGNALS + #endif + #include + QSK_QT_PRIVATE_END + + static void QQuickShapes_initializeModule() + { + QQuickShapesModule::defineModule(); + } + +#else + extern void QQuickShapes_initializeModule(); +#endif + +Q_CONSTRUCTOR_FUNCTION( QQuickShapes_initializeModule ) + +namespace +{ + /* + Qt offers QGradient compatible shaders in the quickshapes module. + So why reimplementing the wheel ... + + Those shaders ( f.e lineargradient.frag ) want to have a lookup table + with the interpolated colors, being uploaded as a texture. + A 1 dimensional 256x1 texture is how Chrome, Firefox, and Android render + gradients - so let's do the same. + */ + class GradientImage : public QImage + { + public: + GradientImage( const QGradientStops& stops ) + : QImage( 256, 1, QImage::Format_RGBA8888_Premultiplied ) + { + constexpr int numColors = 256; + auto colorTable = reinterpret_cast< uint* >( bits() ); + + int index1, index2; + QRgb rgb1, rgb2; + + index1 = index2 = qRound( stops[0].first * numColors ); + rgb1 = rgb2 = stops[0].second.rgba(); + + if ( index1 > 0 ) + { + const auto v = value( rgb1 ); + + for ( int i = 0; i < index1; i++ ) + colorTable[i] = v; + } + + for ( int i = 1; i < stops.count(); i++ ) + { + const auto& stop = stops[i]; + + index2 = qRound( stop.first * numColors ); + rgb2 = stop.second.rgba(); + + const auto n = index2 - index1; + + for ( int j = 0; j < n; j++ ) + { + const auto rgb = QskRgb::interpolated( rgb1, rgb2, qreal( j ) / n ); + colorTable[ index1 + j] = value( rgb ); + } + + index1 = index2; + rgb1 = rgb2; + } + + if ( index1 < numColors - 1 ) + { + const auto v = value( rgb1 ); + + for ( int i = index1; i < numColors ; i++ ) + colorTable[i] = v; + } + } + + private: + inline uint value( const QRgb rgb ) const + { + return ARGB2RGBA( qPremultiply( rgb ) ); + } + }; + + class GradientTexture : public QSGPlainTexture + { + public: + GradientTexture( const QGradientStops& stops, QGradient::Spread spread ) + { + setImage( GradientImage( stops ) ); + + const auto wrapMode = this->wrapMode( spread ); + + setHorizontalWrapMode( wrapMode ); + setVerticalWrapMode( wrapMode ); + + setFiltering( QSGTexture::Linear ); + }; + + private: + static inline QSGTexture::WrapMode wrapMode( QGradient::Spread spread ) + { + switch ( spread ) + { + case QGradient::RepeatSpread: + return QSGTexture::Repeat; + + case QGradient::ReflectSpread: + return QSGTexture::MirroredRepeat; + + default: + return QSGTexture::ClampToEdge; + } + } + }; + + class TextureHashKey + { + public: + inline bool operator==( const TextureHashKey& other ) const + { + return rhi == other.rhi && spread == other.spread && stops == other.stops; + } + + const void* rhi; + const QGradientStops stops; + const QGradient::Spread spread; + }; + + inline size_t qHash( const TextureHashKey& key, size_t seed = 0 ) + { + size_t valus = seed + key.spread; + + for ( const auto& stop : key.stops ) + valus += stop.second.rgba(); + + return valus; + } + + class TextureCache + { + public: + static TextureCache* instance() + { + static TextureCache* s_instance = nullptr; + if ( s_instance == nullptr ) + { + s_instance = new TextureCache(); + + /* + For OpenGL we coud fiddle around with QOpenGLSharedResource + while with RHI we would have QRhi::addCleanupCallback + + But let's keep things simple for the moment. TODO ... + */ + qAddPostRoutine( cleanup ); + } + + return s_instance; + } + + ~TextureCache() + { + qDeleteAll( m_hashTable ); + } + + GradientTexture* texture( const void* rhi, + const QGradientStops& stops, QGradient::Spread spread ) + { + const TextureHashKey key { rhi, stops, spread }; + + auto texture = m_hashTable[key]; + if ( texture == nullptr ) + { + texture = new GradientTexture( stops, spread ); + m_hashTable[ key ] = texture; + } + + return texture; + } + + private: + static void cleanup() + { + delete instance(); + } + + QHash< TextureHashKey, GradientTexture* > m_hashTable; + }; +} + +namespace +{ + class GradientMaterial : public QskGradientMaterial + { + public: + GradientMaterial( QGradient::Type type, + const QGradientStops& stops, QGradient::Spread spread ) + : QskGradientMaterial( type, stops, spread ) + { + setFlag( Blending | RequiresFullMatrix ); + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + setFlag( QSGMaterial::SupportsRhiShader, true ); +#endif + } + + int compare( const QSGMaterial* other ) const override + { + const auto mat = static_cast< const GradientMaterial* >( other ); + + if ( ( spread() == mat->spread() ) && ( stops() == mat->stops() ) ) + return 0; + + return QSGMaterial::compare( other ); + } + +#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 ) + // make Qt 5/6 APIs matchaing + + QSGMaterialShader* createShader( + QSGRendererInterface::RenderMode ) const override final + { + return createShader(); + } + + virtual QSGMaterialShader* createShader() const = 0; +#endif + }; + +#ifdef SHADER_GL + + class GradientShaderGL : public QSGMaterialShader + { + public: + void setShaderFiles( const char* name ) + { + static const QString root( ":/qt-project.org/shapes/shaders/" ); + + setShaderSourceFile( QOpenGLShader::Vertex, root + name + ".vert" ); + setShaderSourceFile( QOpenGLShader::Fragment, root + name + ".frag" ); + } + + void initialize() override + { + m_opacityId = program()->uniformLocation( "opacity" ); + m_matrixId = program()->uniformLocation( "matrix" ); + } + + void updateState( const RenderState& state, + QSGMaterial* newMaterial, QSGMaterial* ) override final + { + auto p = program(); + auto material = static_cast< GradientMaterial* >( newMaterial ); + + if ( state.isOpacityDirty() ) + p->setUniformValue( m_opacityId, state.opacity() ); + + if ( state.isMatrixDirty() ) + p->setUniformValue(m_matrixId, state.combinedMatrix() ); + + updateUniformValues( material ); + + auto texture = TextureCache::instance()->texture( + nullptr, material->stops(), material->spread() ); + texture->bind(); + } + + char const* const* attributeNames() const override final + { + static const char* const attr[] = { "vertexCoord", nullptr }; + return attr; + } + + virtual void updateUniformValues( const GradientMaterial* ) = 0; + + protected: + int m_opacityId = -1; + int m_matrixId = -1; + }; +#endif + +#ifdef SHADER_RHI + class GradientShaderRhi : public RhiShader + { + public: + void setShaderFiles( const char* name ) + { + static const QString root( ":/qt-project.org/shapes/shaders_ng/" ); + + setShaderFileName( VertexStage, root + name + ".vert.qsb" ); + setShaderFileName( FragmentStage, root + name + ".frag.qsb" ); + } + + void updateSampledImage( RenderState& state, int binding, + QSGTexture** texture, QSGMaterial* newMaterial, QSGMaterial*) override final + { + if ( binding != 1 ) + return; + + auto material = static_cast< const GradientMaterial* >( newMaterial ); + + auto txt = TextureCache::instance()->texture( + state.rhi(), material->stops(), material->spread() ); + +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + txt->updateRhiTexture( state.rhi(), state.resourceUpdateBatch() ); +#else + txt->commitTextureOperations( state.rhi(), state.resourceUpdateBatch() ); +#endif + + *texture = txt; + } + }; +#endif +} + +namespace +{ + class LinearMaterial final : public GradientMaterial + { + public: + LinearMaterial( + const QGradientStops& stops, QGradient::Spread spread, + const QPointF& start, const QPointF& stop ) + : GradientMaterial( QGradient::LinearGradient, stops, spread ) + , m_start( start ) + , m_stop( stop ) + { + } + + QSGMaterialShader* createShader() const override; + + QSGMaterialType* type() const override + { + static QSGMaterialType type; + return &type; + } + + int compare( const QSGMaterial* other ) const override + { + const auto mat = static_cast< const LinearMaterial* >( other ); + + if ( ( m_start != mat->m_start ) || ( m_stop != mat->m_stop ) ) + return QSGMaterial::compare( other ); + else + return GradientMaterial::compare( other ); + } + + const QVector2D m_start; + const QVector2D m_stop; + }; + +#ifdef SHADER_GL + class LinearShaderGL final : public GradientShaderGL + { + public: + LinearShaderGL() + { + setShaderFiles( "lineargradient" ); + } + + void initialize() override + { + GradientShaderGL::initialize(); + + m_startId = program()->uniformLocation( "gradStart" ); + m_stopId = program()->uniformLocation( "gradEnd" ); + } + + void updateUniformValues( const GradientMaterial* newMaterial ) override + { + auto material = static_cast< const LinearMaterial* >( newMaterial ); + + program()->setUniformValue( m_startId, material->m_start ); + program()->setUniformValue( m_stopId, material->m_stop ); + } + + private: + int m_startId = -1; + int m_stopId = -1; + }; +#endif + +#ifdef SHADER_RHI + class LinearShaderRhi final : public GradientShaderRhi + { + public: + LinearShaderRhi() + { + setShaderFiles( "lineargradient" ); + } + + bool updateUniformData( RenderState& state, + QSGMaterial* newMaterial, QSGMaterial* oldMaterial ) override + { + auto matNew = static_cast< LinearMaterial* >( newMaterial ); + auto matOld = static_cast< LinearMaterial* >( oldMaterial ); + + Q_ASSERT( state.uniformData()->size() >= 84 ); + + auto data = state.uniformData()->data(); + bool changed = false; + + if ( state.isMatrixDirty() ) + { + const auto matrix = state.combinedMatrix(); + memcpy( data + 0, matrix.constData(), 64 ); + + changed = true; + } + + if ( matOld == nullptr || matNew->m_start != matOld->m_start ) + { + memcpy( data + 64, &matNew->m_start, 8 ); + changed = true; + } + + if ( matOld == nullptr || matNew->m_stop != matOld->m_stop ) + { + memcpy( data + 72, &matNew->m_stop, 8 ); + changed = true; + } + + if ( state.isOpacityDirty() ) + { + const float opacity = state.opacity(); + memcpy( data + 80, &opacity, 4 ); + + changed = true; + } + + return changed; + } + }; +#endif + + QSGMaterialShader* LinearMaterial::createShader() const + { +#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 ) + if ( !( flags() & QSGMaterial::RhiShaderWanted ) ) + return new LinearShaderGL; +#endif + return new LinearShaderRhi; + } +} + +namespace +{ + class RadialMaterial final : public GradientMaterial + { + public: + RadialMaterial( + const QGradientStops& stops, QGradient::Spread spread, + const QPointF& center, qreal centerRadius, + const QPointF& focalPoint, qreal focalRadius ) + : GradientMaterial( QGradient::RadialGradient, stops, spread ) + , m_center( center ) + , m_focalPoint( focalPoint ) + , m_centerRadius( centerRadius ) + , m_focalRadius( focalRadius ) + { + } + + QSGMaterialShader* createShader() const override; + + QSGMaterialType* type() const override + { + static QSGMaterialType type; + return &type; + } + + int compare( const QSGMaterial* other ) const override + { + const auto mat = static_cast< const RadialMaterial* >( other ); + + if ( ( m_center != mat->m_center ) + || ( m_focalPoint != mat->m_focalPoint ) + || qskFuzzyCompare( m_centerRadius, mat->m_centerRadius ) + || qskFuzzyCompare( m_focalRadius, mat->m_focalRadius ) ) + { + return QSGMaterial::compare( other ); + } + else + { + return GradientMaterial::compare( other ); + } + } + + const QVector2D m_center; + const QVector2D m_focalPoint; + const float m_centerRadius; + const float m_focalRadius; + }; + +#ifdef SHADER_GL + class RadialShaderGL final : public GradientShaderGL + { + public: + RadialShaderGL() + { + setShaderFiles( "radialgradient" ); + } + + void initialize() override + { + GradientShaderGL::initialize(); + + auto p = program(); + + m_centerPointId = p->uniformLocation( "translationPoint" ); + m_focalPointId = p->uniformLocation( "focalToCenter" ); + m_centerRadiusId = p->uniformLocation( "centerRadius" ); + m_focalRadiusId = p->uniformLocation( "focalRadius" ); + } + + void updateUniformValues( const GradientMaterial* newMaterial ) override + { + auto material = static_cast< const RadialMaterial* >( newMaterial ); + + auto p = program(); + + p->setUniformValue( m_centerPointId, material->m_center ); + p->setUniformValue( m_centerRadiusId, material->m_centerRadius); + p->setUniformValue( m_focalRadiusId, material->m_focalRadius); + p->setUniformValue( m_focalPointId, material->m_focalPoint); + } + + private: + int m_centerPointId = -1; + int m_focalPointId = -1; + int m_centerRadiusId = -1; + int m_focalRadiusId = -1; + }; +#endif + +#ifdef SHADER_RHI + class RadialShaderRhi final : public GradientShaderRhi + { + public: + RadialShaderRhi() + { + setShaderFiles( "radialgradient" ); + } + + bool updateUniformData( RenderState& state, + QSGMaterial* newMaterial, QSGMaterial* oldMaterial) override + { + auto matNew = static_cast< RadialMaterial* >( newMaterial ); + auto matOld = static_cast< RadialMaterial* >( oldMaterial ); + + Q_ASSERT( state.uniformData()->size() >= 92 ); + + auto data = state.uniformData()->data(); + bool changed = false; + + if ( state.isMatrixDirty() ) + { + const auto matrix = state.combinedMatrix(); + memcpy( data + 0, matrix.constData(), 64 ); + + changed = true; + } + + if ( matOld == nullptr || matNew->m_focalPoint != matOld->m_focalPoint ) + { + memcpy( data + 64, &matNew->m_focalPoint, 8 ); + changed = true; + } + + if ( matOld == nullptr || matNew->m_focalPoint != matOld->m_focalPoint ) + { + memcpy( data + 72, &matNew->m_focalPoint, 8 ); + changed = true; + } + + if ( matOld == nullptr || matNew->m_centerRadius != matOld->m_centerRadius ) + { + memcpy( data + 80, &matNew->m_centerRadius, 4); + changed = true; + } + + if ( matOld == nullptr || matNew->m_focalRadius != matOld->m_focalRadius ) + { + memcpy( data + 84, &matOld->m_focalRadius, 4 ); + changed = true; + } + + if ( state.isOpacityDirty() ) + { + const float opacity = state.opacity(); + memcpy( data + 88, &opacity, 4 ); + + changed = true; + } + + return changed; + } + }; +#endif + + QSGMaterialShader* RadialMaterial::createShader() const + { +#ifdef SHADER_GL + if ( !( flags() & QSGMaterial::RhiShaderWanted ) ) + return new RadialShaderGL; +#endif + + return new RadialShaderRhi; + } +} + +namespace +{ + class ConicalMaterial final : public GradientMaterial + { + public: + ConicalMaterial( const QGradientStops& stops, + const QPointF& center, qreal angle ) + : GradientMaterial( QGradient::ConicalGradient, stops, QGradient::PadSpread ) + , m_center( center ) + , m_radians( -qDegreesToRadians( angle ) ) + { + } + + QSGMaterialShader* createShader() const override; + + QSGMaterialType* type() const override + { + static QSGMaterialType type; + return &type; + } + + int compare( const QSGMaterial* other ) const override + { + const auto mat = static_cast< const ConicalMaterial* >( other ); + + if ( ( m_center != mat->m_center ) || qskFuzzyCompare( m_radians, mat->m_radians ) ) + return QSGMaterial::compare( other ); + else + return GradientMaterial::compare( other ); + } + + const QVector2D m_center; + const float m_radians; + }; + +#ifdef SHADER_GL + class ConicalShaderGL final : public GradientShaderGL + { + public: + ConicalShaderGL() + { + setShaderFiles( "conicalgradient" ); + } + + void initialize() override + { + GradientShaderGL::initialize(); + + m_radiansId = program()->uniformLocation( "angle" ); + m_centerPointId = program()->uniformLocation( "translationPoint" ); + } + + void updateUniformValues( const GradientMaterial* newMaterial ) override + { + auto material = static_cast< const ConicalMaterial* >( newMaterial ); + + program()->setUniformValue( m_radiansId, material->m_radians ); + program()->setUniformValue( m_centerPointId, material->m_center ); + } + + private: + int m_radiansId = -1; + int m_centerPointId = -1; + }; +#endif + +#ifdef SHADER_RHI + class ConicalShaderRhi final : public GradientShaderRhi + { + public: + ConicalShaderRhi() + { + setShaderFiles( "conicalgradient" ); + } + + bool updateUniformData( RenderState& state, + QSGMaterial* newMaterial, QSGMaterial* oldMaterial ) override + { + auto matNew = static_cast< ConicalMaterial* >( newMaterial ); + auto matOld = static_cast< ConicalMaterial* >( oldMaterial ); + + Q_ASSERT( state.uniformData()->size() >= 80 ); + + auto data = state.uniformData()->data(); + bool changed = false; + + if ( state.isMatrixDirty() ) + { + const auto matrix = state.combinedMatrix(); + memcpy( data + 0, matrix.constData(), 64 ); + + changed = true; + } + + if ( matOld == nullptr || matNew->m_center != matOld->m_center ) + { + memcpy( data + 64, &matNew->m_center, 8 ); + changed = true; + } + + if ( matOld == nullptr || matNew->m_radians != matOld->m_radians ) + { + memcpy( data + 72, &matNew->m_radians, 4 ); + changed = true; + } + + if ( state.isOpacityDirty() ) + { + const float opacity = state.opacity(); + memcpy( data + 76, &opacity, 4 ); + + changed = true; + } + + return changed; + } + }; +#endif + + QSGMaterialShader* ConicalMaterial::createShader() const + { +#ifdef SHADER_GL + if ( !( flags() & QSGMaterial::RhiShaderWanted ) ) + return new ConicalShaderGL; +#endif + return new ConicalShaderRhi; + } +} + +QskGradientMaterial* QskGradientMaterial::create( const QGradient* gradient ) +{ + switch ( static_cast< int >( gradient->type() ) ) + { + case QGradient::LinearGradient: + { + auto linearGradient = static_cast< const QLinearGradient* >( gradient ); + + return new LinearMaterial( + linearGradient->stops(), linearGradient->spread(), + linearGradient->start(), linearGradient->finalStop() ); + } + + case QGradient::RadialGradient: + { + auto radialGradient = static_cast< const QRadialGradient* >( gradient ); + + return new RadialMaterial( + radialGradient->stops(), radialGradient->spread(), + radialGradient->center(), radialGradient->centerRadius(), + radialGradient->focalPoint(), radialGradient->focalRadius() ); + } + + case QGradient::ConicalGradient: + { + auto conicalGradient = static_cast< const QConicalGradient* >( gradient ); + + return new ConicalMaterial( conicalGradient->stops(), + conicalGradient->center(), conicalGradient->angle() ); + } + } + + return nullptr; +} + + diff --git a/src/nodes/QskGradientMaterial.h b/src/nodes/QskGradientMaterial.h new file mode 100644 index 00000000..8f5f89e1 --- /dev/null +++ b/src/nodes/QskGradientMaterial.h @@ -0,0 +1,56 @@ +/****************************************************************************** + * QSkinny - Copyright (C) 2016 Uwe Rathmann + * This file may be used under the terms of the QSkinny License, Version 1.0 + *****************************************************************************/ + +#ifndef QSK_GRADIENT_MATERIAL +#define QSK_GRADIENT_MATERIAL + +#include "QskGlobal.h" +#include +#include + +class QSK_EXPORT QskGradientMaterial : public QSGMaterial +{ + public: + static QskGradientMaterial* create( const QGradient* ); + + QGradient::Type gradientType() const; + + const QGradientStops& stops() const; + QGradient::Spread spread() const; + + protected: + QskGradientMaterial( QGradient::Type, + const QGradientStops&, QGradient::Spread ); + + private: + const QGradient::Type m_gradientType; + const QGradientStops m_stops; + const QGradient::Spread m_spread; +}; + +inline QskGradientMaterial::QskGradientMaterial( QGradient::Type type, + const QGradientStops& stops, QGradient::Spread spread ) + : m_gradientType( type ) + , m_stops( stops ) + , m_spread( spread ) +{ +} + +inline QGradient::Type QskGradientMaterial::gradientType() const +{ + return m_gradientType; +} + +inline const QGradientStops& QskGradientMaterial::stops() const +{ + return m_stops; +} + +inline QGradient::Spread QskGradientMaterial::spread() const +{ + return m_spread; +} + +#endif diff --git a/src/nodes/QskShapeNode.cpp b/src/nodes/QskShapeNode.cpp index 65a0da16..b87aa8f0 100644 --- a/src/nodes/QskShapeNode.cpp +++ b/src/nodes/QskShapeNode.cpp @@ -4,7 +4,9 @@ *****************************************************************************/ #include "QskShapeNode.h" +#include "QskGradientMaterial.h" +#include #include QSK_QT_PRIVATE_BEGIN @@ -17,30 +19,41 @@ static void qskUpdateGeometry( const QPainterPath& path, QSGGeometry& geometry ) { const auto ts = qTriangulate( path, QTransform(), 1, false ); +#if 1 + geometry.allocate( ts.vertices.size(), ts.indices.size() ); + + auto vertexData = reinterpret_cast< float* >( geometry.vertexData() ); + const auto points = ts.vertices.constData(); + + for ( int i = 0; i < ts.vertices.count(); i++ ) + vertexData[i] = points[i]; + + memcpy( geometry.indexData(), ts.indices.data(), + ts.indices.size() * sizeof( quint16 ) ); +#else /* + As we have to iterate over the vertex buffer to copy qreal to float + anyway we could reorder according to the index buffer and drop + the index buffer then ??? + QTriangleSet: vertices: (x[i[n]], y[i[n]]), (x[j[n]], y[j[n]]), (x[k[n]], y[k[n]]), n = 0, 1, ... QVector vertices; // [x[0], y[0], x[1], y[1], x[2], ...] QVector indices; // [i[0], j[0], k[0], i[1], j[1], k[1], i[2], ...] */ - - geometry.allocate( ts.vertices.size(), ts.indices.size() ); - - auto vertexData = reinterpret_cast< float* >( geometry.vertexData() ); const auto points = ts.vertices.constData(); + const auto indices = reinterpret_cast< const quint16* >( ts.indices.data() ); - /* - As we have to iterate over the vertex buffer to copy qreal to float - anyway we could reorder according to the index buffer and drop - the index buffer then ??? - */ + geometry.allocate( ts.indices.size() ); - for ( int i = 0; i < ts.vertices.count(); ++i ) - vertexData[i] = points[i]; - - memcpy( geometry.indexData(), ts.indices.data(), - ts.indices.size() * sizeof( quint16 ) ); + auto vertexData = geometry.vertexDataAsPoint2D(); + for ( int i = 0; i < ts.indices.size(); i++ ) + { + const int j = 2 * indices[i]; + vertexData[i].set( points[j], points[j + 1] ); + } +#endif } class QskShapeNodePrivate final : public QSGGeometryNodePrivate @@ -82,3 +95,18 @@ void QskShapeNode::updateNode( const QPainterPath& path, const QColor& color ) markDirty( QSGNode::DirtyMaterial ); } } + +void QskShapeNode::updateNode( const QPainterPath& path, const QGradient* gradient ) +{ + if ( true ) // For the moment we always update the geometry. TODO ... + { + qskUpdateGeometry( path, *geometry() ); + markDirty( QSGNode::DirtyGeometry ); + } + + if ( true ) // For the moment we always update the material + { + setMaterial( QskGradientMaterial::create( gradient ) ); + markDirty( QSGNode::DirtyMaterial ); + } +} diff --git a/src/nodes/QskShapeNode.h b/src/nodes/QskShapeNode.h index 303c44bd..37c753b6 100644 --- a/src/nodes/QskShapeNode.h +++ b/src/nodes/QskShapeNode.h @@ -9,6 +9,7 @@ #include "QskGlobal.h" #include +class QGradient; class QColor; class QPainterPath; @@ -19,6 +20,7 @@ class QSK_EXPORT QskShapeNode : public QSGGeometryNode public: QskShapeNode(); + void updateNode( const QPainterPath&, const QGradient* ); void updateNode( const QPainterPath&, const QColor& ); private: diff --git a/src/src.pro b/src/src.pro index 55960141..38773a7e 100644 --- a/src/src.pro +++ b/src/src.pro @@ -3,6 +3,12 @@ TARGET = $$qskLibraryTarget(qskinny) QT += quick quick-private +# We need quickshapes for the shaders for the gradients only, +# As those shaders are more or less trivial # it might be better +# to implement our own shaders to get rid of this extra module dependency + +QT += quickshapes-private + greaterThan( QT_MAJOR_VERSION, 5 ) { QT += opengl-private } @@ -111,8 +117,9 @@ HEADERS += \ nodes/QskScaleRenderer.h \ nodes/QskSGNode.h \ nodes/QskShadedBoxNode.h \ - nodes/QskShapeNode.h \ nodes/QskStrokeNode.h \ + nodes/QskShapeNode.h \ + nodes/QskGradientMaterial.h \ nodes/QskTextNode.h \ nodes/QskTextRenderer.h \ nodes/QskTextureRenderer.h \ @@ -135,8 +142,9 @@ SOURCES += \ nodes/QskScaleRenderer.cpp \ nodes/QskSGNode.cpp \ nodes/QskShadedBoxNode.cpp \ - nodes/QskShapeNode.cpp \ nodes/QskStrokeNode.cpp \ + nodes/QskShapeNode.cpp \ + nodes/QskGradientMaterial.cpp \ nodes/QskTextNode.cpp \ nodes/QskTextRenderer.cpp \ nodes/QskTextureRenderer.cpp \