create image on main thread

This commit is contained in:
Peter Hartmann 2025-01-28 10:43:22 +01:00
parent e36aa93557
commit ed562d092a
5 changed files with 113 additions and 119 deletions

View File

@ -12,31 +12,67 @@ QSK_SUBCONTROL( QskColorPicker, Panel )
QSK_SUBCONTROL( QskColorPicker, ColorPane ) QSK_SUBCONTROL( QskColorPicker, ColorPane )
QSK_SUBCONTROL( QskColorPicker, Selector ) QSK_SUBCONTROL( QskColorPicker, Selector )
using A = QskAspect;
namespace
{
const auto HPos = QskColorPicker::Selector | A::Horizontal;
const auto VPos = QskColorPicker::Selector | A::Vertical;
void createImage( QImage* image, const QSizeF& size, qreal v )
{
if( image->size() != size )
{
*image = QImage( size.width(), size.height(), QImage::Format_RGB32 );
}
QColor color;
float h, s;
for( int x = 0; x < image->width(); x++ )
{
h = static_cast< float >( x ) / image->width();
for( int y = 0; y < image->height(); y++ )
{
s = 1.0 - static_cast< float >( y ) / image->height();
color.setHsvF( h, s, v );
image->setPixel( x, y, color.rgb() );
}
}
}
}
class QskColorPicker::PrivateData class QskColorPicker::PrivateData
{ {
public: public:
qreal value = 255; qreal value = 255;
bool isPressed = false; bool isPressed = false;
int colorChangedEventType; QImage image;
}; };
QskColorPicker::QskColorPicker( QQuickItem* parent ) QskColorPicker::QskColorPicker( QQuickItem* parent )
: Inherited( parent ) : Inherited( parent )
, m_data( new PrivateData ) , m_data( new PrivateData )
{ {
m_data->colorChangedEventType = QEvent::registerEventType();
setAcceptedMouseButtons( Qt::LeftButton ); setAcceptedMouseButtons( Qt::LeftButton );
setBoundaries( 0, 255 ); setBoundaries( 0, 255 );
setPolishOnResize( true );
createImage();
setPositionHint( HPos, -1 );
setPositionHint( VPos, -1 );
} }
QskColorPicker::~QskColorPicker() = default; QskColorPicker::~QskColorPicker() = default;
QColor QskColorPicker::selectedColor() const QColor QskColorPicker::selectedColor() const
{ {
auto* skinlet = static_cast< const QskColorPickerSkinlet* >( effectiveSkinlet() ); if( image().isNull() )
Q_ASSERT( skinlet ); {
return skinlet->selectedColor(); return {};
}
return m_data->image.pixelColor( position().toPoint() );
} }
qreal QskColorPicker::value() const qreal QskColorPicker::value() const
@ -49,9 +85,9 @@ qreal QskColorPicker::valueAsRatio() const
return valueAsRatio( m_data->value ); return valueAsRatio( m_data->value );
} }
int QskColorPicker::colorChangedEventType() const QImage QskColorPicker::image() const
{ {
return m_data->colorChangedEventType; return m_data->image;
} }
void QskColorPicker::setValue( qreal v ) void QskColorPicker::setValue( qreal v )
@ -59,8 +95,10 @@ void QskColorPicker::setValue( qreal v )
if( !qskFuzzyCompare( m_data->value, v ) ) if( !qskFuzzyCompare( m_data->value, v ) )
{ {
m_data->value = v; m_data->value = v;
createImage();
update(); update();
Q_EMIT valueChanged( m_data->value ); Q_EMIT valueChanged( m_data->value );
Q_EMIT selectedColorChanged();
} }
} }
@ -70,22 +108,16 @@ void QskColorPicker::setValueAsRatio( qreal ratio )
setValue( minimum() + ratio * boundaryLength() ); setValue( minimum() + ratio * boundaryLength() );
} }
bool QskColorPicker::event( QEvent* event ) void QskColorPicker::updateLayout()
{ {
if( event->type() == colorChangedEventType() ) createImage();
{ updatePosition( position() );
event->setAccepted( true );
Q_EMIT selectedColorChanged();
return true;
}
return Inherited::event( event );
} }
void QskColorPicker::mousePressEvent( QMouseEvent* event ) void QskColorPicker::mousePressEvent( QMouseEvent* event )
{ {
m_data->isPressed = true; m_data->isPressed = true;
updatePosition( event ); updatePosition( qskMousePosition( event ) );
} }
void QskColorPicker::mouseMoveEvent( QMouseEvent* event ) void QskColorPicker::mouseMoveEvent( QMouseEvent* event )
@ -95,7 +127,7 @@ void QskColorPicker::mouseMoveEvent( QMouseEvent* event )
return; return;
} }
updatePosition( event ); updatePosition( qskMousePosition( event ) );
} }
void QskColorPicker::mouseReleaseEvent( QMouseEvent* ) void QskColorPicker::mouseReleaseEvent( QMouseEvent* )
@ -103,24 +135,51 @@ void QskColorPicker::mouseReleaseEvent( QMouseEvent* )
m_data->isPressed = false; m_data->isPressed = false;
} }
void QskColorPicker::updatePosition( QMouseEvent* event ) void QskColorPicker::updatePosition( const QPointF& point )
{ {
auto p = qskMousePosition( event );
const auto rect = subControlRect( ColorPane ); const auto rect = subControlRect( ColorPane );
p.rx() = qBound( rect.x(), p.x(), rect.right() ); if( rect.isEmpty() )
p.ry() = qBound( rect.y(), p.y(), rect.bottom() ); {
return;
}
const auto oldX = positionHint( Selector | QskAspect::Horizontal ); auto p = point;
const auto oldY = positionHint( Selector | QskAspect::Vertical ); p.rx() = qBound( rect.x(), p.x(), rect.right() - 1.0 );
p.ry() = qBound( rect.y(), p.y(), rect.bottom() - 1.0 );
const auto oldX = positionHint( HPos );
const auto oldY = positionHint( VPos );
if( !qskFuzzyCompare( p.x(), oldX ) || !qskFuzzyCompare( p.y(), oldY ) ) if( !qskFuzzyCompare( p.x(), oldX ) || !qskFuzzyCompare( p.y(), oldY ) )
{ {
setPositionHint( Selector | QskAspect::Horizontal, p.x() ); setPositionHint( HPos, p.x() );
setPositionHint( Selector | QskAspect::Vertical, p.y() ); setPositionHint( VPos, p.y() );
update(); update();
Q_EMIT positionChanged();
Q_EMIT selectedColorChanged();
} }
} }
QPointF QskColorPicker::position() const
{
const auto r = subControlRect( ColorPane );
if( !r.size().isValid() )
{
return {};
}
const auto x = positionHint( HPos );
const auto y = positionHint( VPos );
return { x, y };
}
void QskColorPicker::createImage()
{
const auto r = subControlRect( ColorPane );
::createImage( &m_data->image, r.size(), valueAsRatio() );
}
#include "moc_QskColorPicker.cpp" #include "moc_QskColorPicker.cpp"

View File

@ -28,7 +28,8 @@ class QskColorPicker : public QskBoundedControl
qreal valueAsRatio() const; // [0.0, 1.0] qreal valueAsRatio() const; // [0.0, 1.0]
using QskBoundedControl::valueAsRatio; using QskBoundedControl::valueAsRatio;
int colorChangedEventType() const; QImage image() const;
QPointF position() const;
public Q_SLOTS: public Q_SLOTS:
void setValue( qreal ); void setValue( qreal );
@ -37,15 +38,17 @@ class QskColorPicker : public QskBoundedControl
Q_SIGNALS: Q_SIGNALS:
void valueChanged( qreal ); void valueChanged( qreal );
void selectedColorChanged() const; void selectedColorChanged() const;
void positionChanged() const;
protected: protected:
bool event( QEvent* ) override; void updateLayout() override;
void mousePressEvent( QMouseEvent* ) override; void mousePressEvent( QMouseEvent* ) override;
void mouseMoveEvent( QMouseEvent* ) override; void mouseMoveEvent( QMouseEvent* ) override;
void mouseReleaseEvent( QMouseEvent* ) override; void mouseReleaseEvent( QMouseEvent* ) override;
private: private:
void updatePosition( QMouseEvent* ); void updatePosition( const QPointF& );
void createImage();
class PrivateData; class PrivateData;
std::unique_ptr< PrivateData > m_data; std::unique_ptr< PrivateData > m_data;

View File

@ -22,64 +22,34 @@ namespace
{ {
} }
void paint( QPainter* p, const QSize& size, const void* nodeData ) override void paint( QPainter* p, const QSize&, const void* nodeData ) override
{ {
if( m_image.size() != size ) const Q* q = static_cast< const Q* >( nodeData );
{ p->drawImage( QPointF( 0, 0 ), q->image() );
m_image = QImage( size.width(), size.height(), QImage::Format_RGB32 );
}
QColor color;
float h, s;
const float v = *reinterpret_cast< const int* >( nodeData ) / 255.0;
for( int x = 0; x < m_image.width(); x++ )
{
h = ( float ) x / m_image.width();
for( int y = 0; y < m_image.height(); y++ )
{
s = 1.0 - ( float ) y / m_image.height();
color.setHsvF( h, s, v );
m_image.setPixel( x, y, color.rgb() );
}
}
p->drawImage( QPointF( 0, 0 ), m_image );
} }
void updateNode( QQuickWindow* window, const QRectF& rect, int value ) void updateNode( QQuickWindow* window, const QRectF& rect, const Q* q )
{ {
update( window, rect, QSizeF(), &value ); update( window, rect, QSizeF(), q );
}
QColor selectedColor( const QPointF& p ) const
{
return m_image.pixelColor( p.toPoint() );
} }
protected: protected:
QskHashValue hash( const void* nodeData ) const override QskHashValue hash( const void* nodeData ) const override
{ {
const auto* value = reinterpret_cast< const int* >( nodeData ); const Q* q = static_cast< const Q* >( nodeData );
return *value; const auto r = q->subControlRect( Q::ColorPane );
}
private: QskHashValue h = qHash( r.width() );
QImage m_image; h = qHash( r.height() );
h = qHash( q->value() );
return h;
}
}; };
} }
class QskColorPickerSkinlet::PrivateData
{
public:
QColor selectedColor;
ColorPaneNode* colorPaneNode = nullptr;
};
QskColorPickerSkinlet::QskColorPickerSkinlet( QskSkin* skin ) QskColorPickerSkinlet::QskColorPickerSkinlet( QskSkin* skin )
: Inherited( skin ) : Inherited( skin )
, m_data( new PrivateData )
{ {
setNodeRoles( { PanelRole, ColorPaneRole, SelectorRole } ); setNodeRoles( { PanelRole, ColorPaneRole, SelectorRole } );
} }
@ -103,9 +73,7 @@ QSGNode* QskColorPickerSkinlet::updateSubNode(
} }
case SelectorRole: case SelectorRole:
{ {
auto* n = updateBoxNode( skinnable, node, Q::Selector ); return updateBoxNode( skinnable, node, Q::Selector );
updateSelectedColor( q );
return n;
} }
} }
@ -125,57 +93,26 @@ QRectF QskColorPickerSkinlet::subControlRect(
if( subControl == Q::Selector ) if( subControl == Q::Selector )
{ {
const auto size = q->strutSizeHint( Q::Selector ); const auto s = q->strutSizeHint( Q::Selector );
const auto x = q->positionHint( Q::Selector | QskAspect::Horizontal ); const auto p = q->position();
const auto y = q->positionHint( Q::Selector | QskAspect::Vertical );
QRectF r( { x - size.width() / 2.0, y - size.height() / 2.0 }, size ); const QRectF r( { p.x() - s.width() / 2.0, p.y() - s.height() / 2.0 }, s );
return r; return r;
} }
return Inherited::subControlRect( skinnable, contentsRect, subControl ); return Inherited::subControlRect( skinnable, contentsRect, subControl );
} }
QColor QskColorPickerSkinlet::selectedColor() const
{
return m_data->selectedColor;
}
QSGNode* QskColorPickerSkinlet::updateColorPaneNode( QSGNode* QskColorPickerSkinlet::updateColorPaneNode(
const QskColorPicker* q, QSGNode* node ) const const QskColorPicker* q, QSGNode* node ) const
{ {
m_data->colorPaneNode = QskSGNode::ensureNode< ColorPaneNode >( node ); auto* colorPaneNode = QskSGNode::ensureNode< ColorPaneNode >( node );
const auto rect = q->subControlRect( Q::ColorPane );
m_data->colorPaneNode->updateNode( q->window(), rect, q->value() );
updateSelectedColor( q );
return m_data->colorPaneNode;
}
QPointF QskColorPickerSkinlet::selectorPos( const Q* q ) const
{
const auto x = q->positionHint( Q::Selector | QskAspect::Horizontal );
const auto y = q->positionHint( Q::Selector | QskAspect::Vertical );
QPointF p( x, y );
const auto rect = q->subControlRect( Q::ColorPane ); const auto rect = q->subControlRect( Q::ColorPane );
p.rx() = qBound( rect.x(), p.x(), rect.right() ); colorPaneNode->updateNode( q->window(), rect, q );
p.ry() = qBound( rect.y(), p.y(), rect.bottom() );
return p; return colorPaneNode;
}
void QskColorPickerSkinlet::updateSelectedColor( const Q* q ) const
{
const auto color = m_data->colorPaneNode->selectedColor( selectorPos( q ) );
m_data->selectedColor = color;
auto* e = new QEvent( static_cast< QEvent::Type >( q->colorChangedEventType() ) );
QCoreApplication::postEvent( const_cast< Q* >( q ), e );
} }
#include "moc_QskColorPickerSkinlet.cpp" #include "moc_QskColorPickerSkinlet.cpp"

View File

@ -42,11 +42,6 @@ class QskColorPickerSkinlet : public QskSkinlet
private: private:
QRectF cursorRect( const QskSkinnable*, const QRectF&, int index ) const; QRectF cursorRect( const QskSkinnable*, const QRectF&, int index ) const;
QPointF selectorPos( const QskColorPicker* ) const;
void updateSelectedColor( const QskColorPicker* ) const;
class PrivateData;
std::unique_ptr< PrivateData > m_data;
}; };
#endif #endif

View File

@ -479,7 +479,7 @@ namespace
outerBox->setMargins( 20 ); outerBox->setMargins( 20 );
outerBox->setSpacing( 20 ); outerBox->setSpacing( 20 );
#if 1 #if 1
outerBox->setFixedSize( 700, 500 ); outerBox->setFixedSize( 500, 500 );
#endif #endif
auto* upperBox = new QskLinearBox( Qt::Horizontal, outerBox ); auto* upperBox = new QskLinearBox( Qt::Horizontal, outerBox );
upperBox->setSpacing( 12 ); upperBox->setSpacing( 12 );