gradients testprogram:

- widgets dependency removed
    - implementations for other shaders added
This commit is contained in:
Uwe Rathmann 2022-12-20 16:30:49 +01:00
parent 26e422ac07
commit 0f825209d0
7 changed files with 467 additions and 131 deletions

View File

@ -0,0 +1,204 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#include "GradientQuickShape.h"
#include <QskGradient.h>
#include <QskGradientDirection.h>
QSK_QT_PRIVATE_BEGIN
#if QT_VERSION < QT_VERSION_CHECK( 6, 0, 0 )
#ifndef signals
#define signals Q_SIGNALS
#endif
#endif
#include <private/qquickitem_p.h>
#include <private/qquickshape_p.h>
#include <private/qquickshape_p_p.h>
QSK_QT_PRIVATE_END
namespace
{
class ShapePath : public QQuickShapePath
{
public:
ShapePath( QObject* parent = nullptr )
: QQuickShapePath( parent )
{
setStrokeWidth( 0 );
}
void setRect( const QRectF& rect )
{
auto& path = QQuickShapePathPrivate::get( this )->_path;
path.clear();
path.addRect( rect );
}
void setGradient( const QRectF& rect, const QskGradient& gradient )
{
auto d = QQuickShapePathPrivate::get( this );
delete d->sfp.fillGradient;
d->sfp.fillGradient = createShapeGradient( rect, gradient );
d->sfp.fillGradient->setParent( this );
}
private:
QQuickShapeGradient* createShapeGradient(
const QRectF& rect, const QskGradient& gradient ) const
{
QQuickShapeGradient* shapeGradient = nullptr;
const auto qtGradient = gradient.toQGradient( rect );
switch( qtGradient.type() )
{
case QGradient::LinearGradient:
{
auto& linearGradient =
*static_cast< const QLinearGradient* >( &qtGradient );
auto g = new QQuickShapeLinearGradient();
g->setX1( linearGradient.start().x() );
g->setY1( linearGradient.start().y() );
g->setX2( linearGradient.finalStop().x() );
g->setY2( linearGradient.finalStop().y() );
shapeGradient = g;
break;
}
case QGradient::RadialGradient:
{
auto& radialGradient =
*static_cast< const QRadialGradient* >( &qtGradient );
auto g = new QQuickShapeRadialGradient();
g->setCenterX( radialGradient.center().x() );
g->setCenterY( radialGradient.center().y() );
g->setFocalX( radialGradient.center().x() );
g->setFocalY( radialGradient.center().y() );
g->setCenterRadius( radialGradient.radius() );
g->setFocalRadius( radialGradient.radius() );
shapeGradient = g;
break;
}
case QGradient::ConicalGradient:
{
auto& conicalGradient =
*static_cast< const QConicalGradient* >( &qtGradient );
auto g = new QQuickShapeConicalGradient();
g->setCenterX( conicalGradient.center().x() );
g->setCenterY( conicalGradient.center().y() );
g->setAngle( conicalGradient.angle() );
shapeGradient = g;
break;
}
default:
break;
}
shapeGradient->setSpread(
static_cast< QQuickShapeGradient::SpreadMode >( gradient.spread() ) );
/*
QQuickGradient has been made in the early days of Qt5 for the QML
use case. Everything - even each stop - is a QObject.
*/
const auto qtStops = qskToQGradientStops( gradient.stops() );
for ( const auto& stop : qtStops )
{
class MyGradient : public QObject
{
public:
QList< QQuickGradientStop* > m_stops;
};
auto s = new QQuickGradientStop( shapeGradient );
s->setPosition( stop.first );
s->setColor( stop.second );
reinterpret_cast< MyGradient* >( shapeGradient )->m_stops += s;
}
return shapeGradient;
}
};
class ShapeItem : public QQuickShape
{
public:
ShapeItem()
{
auto d = QQuickShapePrivate::get( this );
d->sp += new ShapePath( this );
}
QSGNode* updateShapeNode( QQuickWindow* window, const QRectF& rect,
const QskGradient& gradient, QSGNode* node )
{
auto d = QQuickShapePrivate::get( this );
ShapePath path;
path.setRect( rect );
path.setGradient( rect, gradient );
d->sp += &path;
d->spChanged = true;
d->refWindow( window );
updatePolish();
node = QQuickShape::updatePaintNode( node, nullptr );
d->derefWindow();
d->sp.clear();
return node;
}
private:
QSGNode* updatePaintNode( QSGNode*, UpdatePaintNodeData* ) override
{
Q_ASSERT( false );
return nullptr;
}
};
}
Q_GLOBAL_STATIC( ShapeItem, shapeItem )
QSGNode* GradientQuickShape::updateNode( QQuickWindow* window,
const QRectF& rect, const QskGradient& gradient, QSGNode* node )
{
/*
Unfortunately the different materials for the gradients are hidden
in private classes of the quickshape module, and can't be accessed
from application code. Hard to understand why such basic functionality
is not offered like QSGFlatColorMaterial and friends. Anyway - we have
QskGradientMaterial now ...
But for the purpose of comparing our shaders with those from quickshape we
use a static QQuickShape to create/update scene graph node, that actually
belong to a different QQuickItem.
*/
return shapeItem->updateShapeNode( window, rect, gradient, node );
}

View File

@ -0,0 +1,17 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#pragma once
class QskGradient;
class QSGNode;
class QQuickWindow;
class QRectF;
namespace GradientQuickShape
{
QSGNode* updateNode( QQuickWindow*, const QRectF&,
const QskGradient&, QSGNode* );
}

View File

@ -0,0 +1,132 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#include "GradientView.h"
#ifdef SHAPE_GRADIENT
#include "GradientQuickShape.h"
#endif
#include <QskPaintedNode.h>
#include <QskRectangleNode.h>
#include <QskBoxFillNode.h>
#include <QskGradient.h>
#include <QskGradientDirection.h>
#include <QBrush>
#include <QPainter>
namespace
{
template< typename Node >
inline Node* gradientNode( QSGNode* node )
{
if ( node == nullptr )
node = new Node();
return static_cast< Node* >( node );
}
class PaintedNode final : public QskPaintedNode
{
public:
void updateNode( QQuickWindow* window,
const QRectF& rect, const QskGradient& gradient )
{
update( window, rect, QSizeF(), &gradient );
}
QskHashValue hash( const void* nodeData ) const override
{
const auto gradient = reinterpret_cast< const QskGradient* >( nodeData );
return gradient->hash();
}
protected:
void paint( QPainter* painter, const QSize& size, const void* nodeData ) override
{
const auto gradient = reinterpret_cast< const QskGradient* >( nodeData );
const QRect rect( 0, 0, size.width(), size.height() );
painter->fillRect( rect, gradient->toQGradient( rect ) );
}
};
}
GradientView::GradientView( NodeType nodeType, QQuickItem* parent )
: QQuickItem( parent )
, m_nodeType( nodeType )
{
setFlag( QQuickItem::ItemHasContents, true );
}
GradientView::NodeType GradientView::GradientView::nodeType() const
{
return m_nodeType;
}
void GradientView::setGradient( const QskGradient& gradient )
{
m_gradient = gradient;
update();
}
QskGradient GradientView::gradient() const
{
return m_gradient;
}
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
void GradientView::geometryChange( const QRectF&, const QRectF& )
#else
void GradientView::geometryChanged( const QRectF&, const QRectF& )
#endif
{
update();
}
QSGNode* GradientView::updatePaintNode(
QSGNode* oldNode, QQuickItem::UpdatePaintNodeData* )
{
const QRectF rect( 0, 0, width(), height() );
switch( m_nodeType )
{
case Painted:
{
auto node = gradientNode< PaintedNode >( oldNode );
node->updateNode( window(), rect, m_gradient );
return node;
}
case Rectangle:
{
auto node = gradientNode< QskRectangleNode >( oldNode );
node->updateNode( rect, m_gradient );
return node;
}
case BoxFill:
{
auto node = gradientNode< QskBoxFillNode >( oldNode );
node->updateNode( rect, m_gradient );
return node;
}
#ifdef SHAPE_GRADIENT
case Shape:
{
return GradientQuickShape::updateNode(
window(), rect, m_gradient, oldNode );
}
#endif
default:
break;
}
return nullptr;
}
#include "moc_GradientView.cpp"

View File

@ -0,0 +1,49 @@
/******************************************************************************
* QSkinny - Copyright (C) 2016 Uwe Rathmann
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#pragma once
#include <QskGradient.h>
#include <QQuickItem>
class GradientView : public QQuickItem
{
Q_OBJECT
Q_PROPERTY( QskGradient gradient READ gradient WRITE setGradient )
public:
enum NodeType
{
Painted,
Rectangle,
BoxFill,
#ifdef SHAPE_GRADIENT
Shape,
#endif
NumNodeTypes
};
Q_ENUM( NodeType )
GradientView( NodeType, QQuickItem* parent = nullptr );
NodeType nodeType() const;
void setGradient( const QskGradient& );
QskGradient gradient() const;
protected:
QSGNode* updatePaintNode( QSGNode*, QQuickItem::UpdatePaintNodeData* ) override;
#if QT_VERSION >= QT_VERSION_CHECK( 6, 0, 0 )
void geometryChange( const QRectF&, const QRectF& ) override;
#else
void geometryChanged( const QRectF&, const QRectF& ) override;
#endif
private:
const NodeType m_nodeType;
QskGradient m_gradient;
};

View File

@ -1,16 +1,18 @@
CONFIG += qskexample
QT += widgets quickwidgets
HEADERS +=
HEADERS += \
GradientView.h
SOURCES += \
GradientView.cpp \
main.cpp
linux {
qtHaveModule(quickshapes_private) {
pedantic {
QMAKE_CXXFLAGS += -isystem $$[QT_INSTALL_HEADERS]/QtQuickWidgets
QMAKE_CXXFLAGS += -isystem $$[QT_INSTALL_HEADERS]/QtWidgets
}
QT += quickshapes_private
HEADERS += GradientQuickShape.h
SOURCES += GradientQuickShape.cpp
DEFINES += SHAPE_GRADIENT
}

View File

@ -3,147 +3,79 @@
* This file may be used under the terms of the 3-clause BSD License
*****************************************************************************/
#include "GradientView.h"
#include <SkinnyNamespace.h>
#include <QskControl.h>
#include <QskGradient.h>
#include <QskLinearBox.h>
#include <QskWindow.h>
#include <QWidget>
#include <QApplication>
#include <QQuickWidget>
#include <QHBoxLayout>
#include <QPainter>
#include <QGuiApplication>
class BoxQsk: public QQuickWidget
namespace
{
public:
BoxQsk( QWidget* parent = nullptr )
: QQuickWidget( parent )
class MainView : public QskLinearBox
{
setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored );
Q_OBJECT
setContentsMargins( QMargins() );
setResizeMode( QQuickWidget::SizeRootObjectToView );
m_control = new QskControl();
setContent( QUrl(), nullptr, m_control );
}
void showGradient( const QGradient& gradient )
{
QskGradient qskGradient( gradient );
/*
Eliminate the useless offsets that have been added to work around
QGradients limitation to have stops at the same position
*/
auto stops = qskGradient.stops();
for ( int i = 1; i < stops.count(); i++ )
public:
MainView( QQuickItem* parent = nullptr )
: QskLinearBox( Qt::Horizontal, 2, parent )
{
if ( stops[i].position() - stops[i-1].position() < 1e-5 )
stops[i].setPosition( stops[i-1].position() );
for ( int i = 0; i < GradientView::NumNodeTypes; i++ )
{
const auto nodeType = static_cast< GradientView::NodeType >( i );
m_views[i] = new GradientView( nodeType, this );
}
showColors( { Qt::green, Qt::red, Qt::yellow, Qt::cyan, Qt::darkCyan } );
}
qskGradient.setStops( stops );
m_control->setBackground( qskGradient );
}
private:
QskControl* m_control;
};
class BoxQt: public QWidget
{
public:
BoxQt( QWidget* parent = nullptr )
: QWidget( parent )
{
setContentsMargins( QMargins() );
setSizePolicy( QSizePolicy::Ignored, QSizePolicy::Ignored );
}
void showGradient( const QGradient& gradient )
{
m_gradient = gradient;
m_gradient.setCoordinateMode( QGradient::ObjectMode );
update();
}
protected:
void paintEvent( QPaintEvent* ) override
{
QPainter painter( this );
painter.fillRect( contentsRect(), m_gradient );
}
private:
QGradient m_gradient;
};
class GradientBox: public QWidget
{
Q_OBJECT
public:
GradientBox( QWidget* parent = nullptr )
: QWidget( parent )
{
m_qtBox = new BoxQt();
m_qskBox = new BoxQsk();
auto layout = new QHBoxLayout( this );
layout->addWidget( m_qtBox );
layout->addWidget( m_qskBox );
showGradient( { Qt::green, Qt::red, Qt::yellow, Qt::cyan, Qt::darkCyan } );
}
void showGradient( const QVector< QColor >& colors )
{
const auto step = 1.0 / colors.size();
QGradientStops stops;
for ( int i = 0; i < colors.size(); i++ )
void showColors( const QVector< QColor >& colors )
{
auto pos = i * step;
if ( i > 0 )
pos += 1e-6; // QGradient does not support stops at the same position
const auto step = 1.0 / colors.size();
stops += { pos, colors[i] };
stops += { ( i + 1 ) * step, colors[i] };
QskGradientStops stops;
for ( int i = 0; i < colors.size(); i++ )
{
stops += { i * step, colors[i] };
stops += { ( i + 1 ) * step, colors[i] };
}
QskGradient gradient;
gradient.setLinearDirection( 0.0, 0.0, 1.0, 1.0 );
gradient.setSpread( QskGradient::RepeatSpread );
gradient.setStops( stops );
showGradient( gradient );
}
QLinearGradient gradient( 0.0, 0.0, 0.5, 0.5 );
gradient.setSpread( QGradient::ReflectSpread );
gradient.setStops( stops );
public Q_SLOTS:
void showGradient( const QskGradient& gradient )
{
for ( auto view : m_views )
{
if ( view )
view->setGradient( gradient );
}
}
showGradient( gradient );
}
public Q_SLOTS:
void showGradient( const QGradient& gradient )
{
m_qtBox->showGradient( gradient );
m_qskBox->showGradient( gradient );
}
private:
BoxQt* m_qtBox;
BoxQsk* m_qskBox;
};
private:
GradientView* m_views[ GradientView::NumNodeTypes ];
};
}
int main( int argc, char** argv )
{
QApplication app( argc, argv );
QGuiApplication app( argc, argv );
Skinny::init(); // we need a skin
GradientBox box;
box.resize( 600, 600 );
box.show();
QskWindow window;
window.addItem( new MainView() );
window.resize( 600, 600 );
window.show();
return app.exec();
}

View File

@ -4,6 +4,7 @@ SUBDIRS += \
anchors \
dials \
dialogbuttons \
gradients \
invoker \
inputpanel \
images \
@ -19,6 +20,5 @@ qtHaveModule(webengine) {
qtHaveModule(quickwidgets) {
SUBDIRS += \
grids \
gradients
grids
}