QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskArcRenderNode.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskArcRenderNode.h"
7#include "QskGradient.h"
8#include "QskArcRenderer.h"
9#include "QskArcMetrics.h"
10#include "QskGradient.h"
11#include "QskSGNode.h"
12#include "QskRgbValue.h"
13#include "QskFillNodePrivate.h"
14
15static inline bool qskHasBorder( qreal width, const QColor& color )
16{
17 return ( width > 0.0 ) && QskRgb::isVisible( color );
18}
19
20class QskArcRenderNodePrivate final : public QskFillNodePrivate
21{
22 public:
23 inline void resetNode( QskArcRenderNode* node )
24 {
25 m_metricsHash = m_colorsHash = 0;
26 node->resetGeometry();
27 }
28
29 inline bool updateMetrics( const QRectF& rect,
30 const QskArcMetrics& metrics, bool radial, qreal borderWidth )
31 {
32 QskHashValue hash = 13000;
33
34 hash = qHashBits( &rect, sizeof( rect ), hash );
35 hash = metrics.hash( hash );
36 hash = qHash( radial, hash );
37 hash = qHash( borderWidth, hash );
38
39 return updateHash( m_metricsHash, hash );
40 }
41
42 inline bool updateColors( const QColor& borderColor, const QskGradient& gradient )
43 {
44 QskHashValue hash = 13000;
45
46 if ( QskRgb::isVisible( borderColor ) )
47 hash = qHashBits( &borderColor, sizeof( borderColor ), hash );
48
49 if ( gradient.isVisible() )
50 hash = gradient.hash( hash );
51
52 return updateHash( m_colorsHash, hash );
53 }
54
55 private:
56 inline bool updateHash( QskHashValue& value, const QskHashValue newValue ) const
57 {
58 if ( newValue != value )
59 {
60 value = newValue;
61 return true;
62 }
63
64 return false;
65 }
66
67 public:
68 QskHashValue m_metricsHash = 0;
69 QskHashValue m_colorsHash = 0;
70};
71
72QskArcRenderNode::QskArcRenderNode()
73 : QskFillNode( *new QskArcRenderNodePrivate )
74{
75}
76
77QskArcRenderNode::~QskArcRenderNode()
78{
79}
80
81void QskArcRenderNode::updateFilling( const QRectF& rect,
82 const QskArcMetrics& metrics, const QskGradient& gradient )
83{
84 updateFilling( rect, metrics, false, 0.0, gradient );
85}
86
87void QskArcRenderNode::updateFilling( const QRectF& rect,
88 const QskArcMetrics& arcMetrics, bool radial,
89 qreal borderWidth, const QskGradient& gradient )
90{
91 Q_D( QskArcRenderNode );
92
93 if ( rect.isEmpty() || arcMetrics.isNull() || !gradient.isVisible() )
94 {
95 d->resetNode( this );
96 return;
97 }
98
99 const auto metrics = arcMetrics.toAbsolute( rect.size() );
100
101 if ( borderWidth >= 0.5 * metrics.thickness() )
102 {
103 d->resetNode( this );
104 return;
105 }
106
107 bool coloredGeometry = hasHint( PreferColoredGeometry );
108 if ( coloredGeometry )
109 {
110 // not all gradients are supported by the renderer
111 coloredGeometry = QskArcRenderer::isGradientSupported( rect, metrics, gradient );
112 }
113 else
114 {
115 // QskGradient::Stops is specific for QskArcRenderer
116 coloredGeometry = ( gradient.type() == QskGradient::Stops );
117 }
118
119 bool dirtyGeometry = d->updateMetrics( rect, metrics, radial, borderWidth );
120 bool dirtyMaterial = d->updateColors( QColor(), gradient );
121
122 if ( coloredGeometry != isGeometryColored() )
123 dirtyGeometry = dirtyMaterial = true;
124
125 if ( dirtyGeometry || dirtyMaterial )
126 {
127 if ( coloredGeometry )
128 {
129 setColoring( QskFillNode::Polychrome );
130
131 QskArcRenderer::setColoredFillLines( rect, metrics, radial,
132 borderWidth, gradient, *geometry() );
133
134 markDirty( QSGNode::DirtyGeometry );
135 }
136 else
137 {
138 setColoring( rect, gradient );
139
140 if ( dirtyGeometry )
141 {
142 QskArcRenderer::setFillLines( rect, metrics,
143 radial, borderWidth, *geometry() );
144
145 markDirty( QSGNode::DirtyGeometry );
146 }
147 }
148 }
149}
150
151void QskArcRenderNode::updateBorder( const QRectF& rect,
152 const QskArcMetrics& arcMetrics, bool radial,
153 qreal borderWidth, const QColor& borderColor )
154{
155 Q_D( QskArcRenderNode );
156
157 if ( rect.isEmpty() || arcMetrics.isNull()
158 || !qskHasBorder( borderWidth, borderColor ) )
159 {
160 d->resetNode( this );
161 return;
162 }
163
164 const bool coloredGeometry = hasHint( PreferColoredGeometry );
165
166 bool dirtyGeometry = d->updateMetrics( rect, arcMetrics, radial, borderWidth );
167 bool dirtyMaterial = d->updateColors( borderColor, QskGradient() );
168
169 if ( coloredGeometry != isGeometryColored() )
170 dirtyGeometry = dirtyMaterial = true;
171
172 if ( dirtyGeometry || dirtyMaterial )
173 {
174 const auto metrics = arcMetrics.toAbsolute( rect.size() );
175 borderWidth = qMin( borderWidth, 0.5 * metrics.thickness() );
176
177 if ( coloredGeometry )
178 {
179 setColoring( QskFillNode::Polychrome );
180
181 QskArcRenderer::setColoredBorderLines( rect, metrics, radial,
182 borderWidth, borderColor, *geometry() );
183
184 markDirty( QSGNode::DirtyGeometry );
185 }
186 else
187 {
188 setColoring( borderColor );
189
190 if ( dirtyGeometry )
191 {
192 QskArcRenderer::setBorderLines( rect, metrics, radial,
193 borderWidth, *geometry() );
194
195 markDirty( QSGNode::DirtyGeometry );
196 }
197 }
198 }
199}
200
201void QskArcRenderNode::updateArc(
202 const QRectF& rect, const QskArcMetrics& arcMetrics, bool radial,
203 qreal borderWidth, const QColor& borderColor, const QskGradient& gradient )
204{
205 Q_D( QskArcRenderNode );
206
207 const auto metrics = arcMetrics.toAbsolute( rect.size() );
208 if ( rect.isEmpty() || metrics.isNull() )
209 {
210 d->resetNode( this );
211 return;
212 }
213
214 const auto borderMax = 0.5 * metrics.thickness();
215
216 const bool hasFill = gradient.isVisible() && ( borderWidth < borderMax );
217 const bool hasBorder = qskHasBorder( borderWidth, borderColor );
218
219 if ( hasBorder && hasFill )
220 {
221 /*
222 When having a monochrome gradient that has the same color as the
223 border we might be able to use QskFillNode::Monochrome. For the
224 moment this is not implemented
225 */
226 borderWidth = qMin( borderWidth, borderMax );
227
228 const bool isDirty = d->updateMetrics( rect, arcMetrics, radial, borderWidth )
229 || d->updateColors( borderColor, gradient ) || !isGeometryColored();
230
231 if ( isDirty )
232 {
233 setColoring( QskFillNode::Polychrome );
234
235 QskArcRenderer::setColoredBorderAndFillLines( rect, metrics, radial,
236 borderWidth, borderColor, gradient, *geometry() );
237
238 markDirty( QSGNode::DirtyGeometry );
239 }
240 }
241 else if ( hasBorder )
242 {
243 updateBorder( rect, arcMetrics, radial, borderWidth, borderColor );
244 }
245 else if ( hasFill )
246 {
247 updateFilling( rect, arcMetrics, radial, borderWidth, gradient );
248 }
249 else
250 {
251 d->resetNode( this );
252 return;
253 }
254}