QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskBoxBasicStroker.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskBoxBasicStroker.h"
7
8namespace
9{
10 inline int gradientLineCount( const QskGradient& borderGradient )
11 {
12 // only the intermediate gradient lines !
13 return qMax( 0, borderGradient.stepCount() - 1 );
14 }
15
16 inline void setGradientLineAt(
17 Qt::Orientation orientation, qreal x1, qreal y1, qreal x2, qreal y2,
18 const QskGradientStop& stop, QskVertex::ColoredLine* line )
19 {
20 if ( orientation == Qt::Horizontal )
21 {
22 const auto pos = x1 + stop.position() * ( x2 - x1 );
23 line->setLine( pos, y1, pos, y2, stop.rgb() );
24 }
25 else
26 {
27 const auto pos = y1 + stop.position() * ( y2 - y1 );
28 line->setLine( x1, pos, x2, pos, stop.rgb() );
29 }
30 }
31
32 inline int edgeToIndex( Qt::Edge edge )
33 { return qCountTrailingZeroBits( static_cast< quint8 >( edge ) ); }
34
35 class CornerIterator : public QskVertex::ArcIterator
36 {
37 public:
38 inline CornerIterator( const QskBoxMetrics& metrics )
39 : m_corners( metrics.corners )
40 {
41 }
42
43 inline void resetSteps( int corner, bool inverted = false )
44 {
45 reset( m_corners[ corner ].stepCount, inverted );
46 }
47
48 inline void setBorderLine( int corner, QskVertex::Line* line ) const
49 {
50 const auto& c = m_corners[ corner ];
51
52 line->setLine( c.xInner( cos() ), c.yInner( sin() ),
53 c.xOuter( cos() ), c.yOuter( sin() ) );
54 }
55
56 protected:
57 const QskBoxMetrics::Corner* m_corners;
58 };
59
60 class CornerIteratorColor : public CornerIterator
61 {
62 public:
63 inline CornerIteratorColor( const QskBoxMetrics& metrics,
64 const QskBoxBorderColors& colors )
65 : CornerIterator( metrics )
66 , m_colors{
67 { colors.top().rgbStart(), colors.left().rgbEnd() },
68 { colors.top().rgbEnd(), colors.right().rgbStart() },
69 { colors.bottom().rgbEnd(), colors.left().rgbStart() },
70 { colors.bottom().rgbStart(), colors.right().rgbEnd() } }
71 {
72 }
73
74 inline void setBorderLine( int corner, QskVertex::ColoredLine* line ) const
75 {
76 const auto& c = m_corners[ corner ];
77
78 line->setLine( c.xInner( cos() ), c.yInner( sin() ),
79 c.xOuter( cos() ), c.yOuter( sin() ), color( corner ) );
80 }
81
82 private:
83 inline QskVertex::Color color( int corner ) const
84 {
85 const auto& cs = m_colors[ corner ];
86
87 if ( cs.first == cs.second )
88 return cs.first;
89
90 const auto ratio = step() / qreal( m_corners[ corner ].stepCount );
91 return cs.first.interpolatedTo( cs.second, ratio );
92 }
93
94 const QPair< QskVertex::Color, QskVertex::Color > m_colors[4];
95 };
96
97 class LineMap
98 {
99 public:
100 inline LineMap( const QskBoxMetrics& metrics )
101 : m_corners( metrics.corners )
102 {
103 }
104
105 inline void setHLine( int corner1, int corner2,
106 qreal cos, qreal sin, QskVertex::Line* line ) const
107 {
108 const qreal y = m_corners[ corner1 ].yInner( sin );
109
110 const qreal x1 = m_corners[ corner1 ].xInner( cos );
111 const qreal x2 = m_corners[ corner2 ].xInner( cos );
112
113 line->setLine( x1, y, x2, y );
114 }
115
116 inline void setVLine( int corner1, int corner2,
117 qreal cos, qreal sin, QskVertex::Line* line ) const
118 {
119 const qreal x = m_corners[ corner1 ].xInner( cos );
120
121 const qreal y1 = m_corners[ corner1 ].yInner( sin );
122 const qreal y2 = m_corners[ corner2 ].yInner( sin );
123
124 line->setLine( x, y1, x, y2 );
125 }
126
127 inline void setLine( int corner1, int corner2,
128 qreal cos, qreal sin, QskVertex::Line* line ) const
129 {
130 const qreal x1 = m_corners[ corner1 ].xInner( cos );
131 const qreal x2 = m_corners[ corner2 ].xInner( cos );
132
133 const qreal y1 = m_corners[ corner1 ].yInner( sin );
134 const qreal y2 = m_corners[ corner2 ].yInner( sin );
135
136 line->setLine( x1, y1, x2, y2 );
137 }
138
139 const QskBoxMetrics::Corner* m_corners;
140 };
141
142 class FillMap
143 {
144 public:
145 inline FillMap( const QskBoxMetrics& metrics,
146 const QskVertex::ColorMap& colorMap )
147 : m_colorMap( colorMap )
148 , m_corners( metrics.corners )
149 {
150 }
151
152 inline void setHLine( int corner1, int corner2,
153 qreal cos, qreal sin, QskVertex::ColoredLine* line ) const
154 {
155 const qreal y = m_corners[ corner1 ].yInner( sin );
156
157 const qreal x1 = m_corners[ corner1 ].xInner( cos );
158 const qreal x2 = m_corners[ corner2 ].xInner( cos );
159
160 m_colorMap.setLine( x1, y, x2, y, line );
161 }
162
163 inline void setVLine( int corner1, int corner2,
164 qreal cos, qreal sin, QskVertex::ColoredLine* line ) const
165 {
166 const qreal x = m_corners[ corner1 ].xInner( cos );
167
168 const qreal y1 = m_corners[ corner1 ].yInner( sin );
169 const qreal y2 = m_corners[ corner2 ].yInner( sin );
170
171 m_colorMap.setLine( x, y1, x, y2, line );
172 }
173
174 inline void setLine( int corner1, int corner2,
175 qreal cos, qreal sin, QskVertex::ColoredLine* line ) const
176 {
177 const qreal x1 = m_corners[ corner1 ].xInner( cos );
178 const qreal x2 = m_corners[ corner2 ].xInner( cos );
179
180 const qreal y1 = m_corners[ corner1 ].yInner( sin );
181 const qreal y2 = m_corners[ corner2 ].yInner( sin );
182
183 m_colorMap.setLine( x1, y1, x2, y2, line );
184 }
185
186 const QskVertex::ColorMap& m_colorMap;
187 const QskBoxMetrics::Corner* m_corners;
188 };
189}
190
191static inline QskVertex::ColoredLine* qskAddGradientLines(
192 const QLineF& l1, const QLineF& l2, const QskGradient& gradient,
194{
195 const auto stops = gradient.stops();
196
197 if ( stops.first().position() > 0.0 )
198 ( lines++ )->setLine( l1, stops.first().rgb() );
199
200 for( const auto& stop : stops )
201 {
202 const auto p1 = l1.p1() + stop.position() * ( l2.p1() - l1.p1() );
203 const auto p2 = l1.p2() + stop.position() * ( l2.p2() - l1.p2() );
204
205 ( lines++ )->setLine( p1, p2, stop.rgb() );
206 }
207
208 if ( stops.last().position() < 1.0 )
209 ( lines++ )->setLine( l2, stops.last().rgb() );
210
211 return lines;
212}
213
214inline void qskSetRectBorderLines( const QRectF& in, const QRectF& out,
215 const QskBoxBorderColors& colors, QskVertex::ColoredLine* lines )
216{
217 const QLineF cl[4] =
218 {
219 { in.right(), in.bottom(), out.right(), out.bottom() },
220 { in.left(), in.bottom(), out.left(), out.bottom() },
221 { in.left(), in.top(), out.left(), out.top() },
222 { in.right(), in.top(), out.right(), out.top() }
223 };
224
225 if ( colors.isMonochrome() )
226 {
227 const QskVertex::Color c = colors.left().rgbStart();
228
229 lines[0].setLine( cl[0], c );
230 lines[1].setLine( cl[1], c );
231 lines[2].setLine( cl[2], c );
232 lines[3].setLine( cl[3], c );
233 lines[4] = lines[ 0 ];
234 }
235 else
236 {
237 lines = qskAddGradientLines( cl[0], cl[1], colors.bottom(), lines );
238 lines = qskAddGradientLines( cl[1], cl[2], colors.left(), lines );
239 lines = qskAddGradientLines( cl[2], cl[3], colors.top(), lines );
240 lines = qskAddGradientLines( cl[3], cl[0], colors.right(), lines );
241 }
242}
243
244template< class Line, class FillMap >
245static inline void qskCreateFill(
246 const QskBoxMetrics& m_metrics, const FillMap& map, Line* lines )
247{
248 using namespace QskVertex;
249 using namespace Qt;
250
251 const bool isHorizontal = ( m_metrics.preferredOrientation == Qt::Horizontal );
252
253 if ( !m_metrics.isInsideRounded )
254 {
255 map.setHLine( TopLeftCorner, TopRightCorner, 0.0, 1.0, lines );
256 map.setHLine( BottomLeftCorner, BottomRightCorner, 0.0, 1.0, lines + 1 );
257 }
258 else if ( m_metrics.isOutsideSymmetric )
259 {
260 const int stepCount = m_metrics.corners[ 0 ].innerStepCount();
261
262 if ( isHorizontal )
263 {
264 Line* l1 = lines + stepCount;
265 Line* l2 = lines + stepCount + 1;
266
267 for ( ArcIterator it( stepCount ); !it.isDone(); ++it )
268 {
269 map.setVLine( TopLeftCorner, BottomLeftCorner, it.cos(), it.sin(), l1++ );
270 map.setVLine( TopRightCorner, BottomRightCorner, it.cos(), it.sin(), l2-- );
271 }
272 }
273 else
274 {
275 Line* l1 = lines;
276 Line* l2 = lines + 2 * stepCount + 1;
277
278 for ( ArcIterator it( stepCount ); !it.isDone(); ++it )
279 {
280 map.setHLine( TopLeftCorner, TopRightCorner, it.cos(), it.sin(), l1++ );
281 map.setHLine( BottomLeftCorner, BottomRightCorner, it.cos(), it.sin(), l2-- );
282 }
283 }
284 }
285 else
286 {
287 auto line = lines;
288 int stepCount;
289
290 if ( isHorizontal )
291 {
292 stepCount = m_metrics.innerStepCount( TopLeftCorner, BottomLeftCorner );
293
294 for ( ArcIterator it( stepCount, true ); !it.isDone(); ++it )
295 map.setLine( TopLeftCorner, BottomLeftCorner, it.cos(), it.sin(), line++ );
296
297 stepCount = m_metrics.innerStepCount( TopRightCorner, BottomRightCorner );
298
299 for ( ArcIterator it( stepCount, false ); !it.isDone(); ++it )
300 map.setLine( TopRightCorner, BottomRightCorner, it.cos(), it.sin(), line++ );
301 }
302 else
303 {
304 stepCount = m_metrics.innerStepCount( TopLeftCorner, TopRightCorner );
305
306 for ( ArcIterator it( stepCount, false ); !it.isDone(); ++it )
307 map.setLine( TopLeftCorner, TopRightCorner, it.cos(), it.sin(), line++ );
308
309 stepCount = m_metrics.innerStepCount( BottomLeftCorner, BottomRightCorner );
310
311 for ( ArcIterator it( stepCount, true ); !it.isDone(); ++it )
312 map.setLine( BottomLeftCorner, BottomRightCorner, it.cos(), it.sin(), line++ );
313 }
314 }
315}
316
317QskBoxBasicStroker::GeometryLayout::GeometryLayout(
318 const QskBoxMetrics& metrics, const QskBoxBorderColors& colors )
319{
320 const struct
321 {
322 Qt::Corner corner;
323 Qt::Edge edge;
324 } order[4] =
325 {
326 // counter clockwise
327 { Qt::BottomRightCorner, Qt::RightEdge },
328 { Qt::TopRightCorner, Qt::TopEdge },
329 { Qt::TopLeftCorner, Qt::LeftEdge },
330 { Qt::BottomLeftCorner, Qt::BottomEdge }
331 };
332
333 /*
334 In case of horizontal filling the lines end at right edge,
335 while for vertical filling it is the bottom edge.
336 */
337 const int index0 = ( metrics.preferredOrientation == Qt::Horizontal ) ? 1 : 0;
338
339 int pos = index0;
340
341 for ( int i = 0; i < 4; i++ )
342 {
343 const int idx = ( index0 + i ) % 4;
344
345 const auto corner = order[ idx ].corner;
346 const auto edge = order[ idx ].edge;
347
348 this->cornerOffsets[ corner ] = pos;
349 pos += metrics.corners[ corner ].stepCount + 1;
350
351 this->edgeOffsets[ edgeToIndex( edge ) ] = pos;
352 pos += gradientLineCount( colors.gradientAt( edge ) );
353 }
354
355 if ( index0 == 0 )
356 {
357 this->closingOffsets[ 0 ] = 0;
358 this->closingOffsets[ 1 ] = pos;
359 this->lineCount = pos + 1;
360 }
361 else
362 {
363 pos--;
364
365 this->closingOffsets[ 0 ] = pos;
366 this->closingOffsets[ 1 ] = 0;
367 this->lineCount = pos + 1;
368 }
369}
370
371QskBoxBasicStroker::QskBoxBasicStroker( const QskBoxMetrics& metrics )
372 : m_metrics( metrics )
373 , m_geometryLayout( metrics, QskBoxBorderColors() )
374 , m_isColored( false )
375{
376}
377
378QskBoxBasicStroker::QskBoxBasicStroker( const QskBoxMetrics& metrics,
379 const QskBoxBorderColors& borderColors )
380 : QskBoxBasicStroker( metrics, borderColors, QskVertex::ColorMap() )
381{
382}
383
384QskBoxBasicStroker::QskBoxBasicStroker( const QskBoxMetrics& metrics,
385 const QskBoxBorderColors& borderColors, const QskVertex::ColorMap& colorMap )
386 : m_metrics( metrics )
387 , m_borderColors( borderColors )
388 , m_colorMap( colorMap )
389 , m_geometryLayout( metrics, m_borderColors )
390 , m_isColored( true )
391{
392}
393
394void QskBoxBasicStroker::setBorderGradientLines(
395 QskVertex::ColoredLine* lines ) const
396{
397 const auto off = m_geometryLayout.edgeOffsets;
398
399 setBorderGradientLines( Qt::TopEdge, lines + off[0] );
400 setBorderGradientLines( Qt::LeftEdge, lines + off[1] );
401 setBorderGradientLines( Qt::RightEdge, lines + off[2] );
402 setBorderGradientLines( Qt::BottomEdge, lines + off[3] );
403}
404
405void QskBoxBasicStroker::setBorderGradientLines(
406 Qt::Edge edge, QskVertex::ColoredLine* lines ) const
407{
408 const auto& gradient = m_borderColors.gradientAt( edge );
409 if( gradient.stepCount() <= 1 )
410 {
411 // everything done as contour lines
412 return;
413 }
414
415 const auto cn = m_metrics.corners;
416
417 qreal x1, x2, y1, y2;
418 Qt::Orientation orientation;
419
420 switch( edge )
421 {
422 case Qt::LeftEdge:
423 {
424 orientation = Qt::Vertical;
425
426 x1 = m_metrics.innerRect.left();
427 x2 = m_metrics.outerRect.left();
428 y1 = cn[ Qt::BottomLeftCorner ].yInner( 0.0 );
429 y2 = cn[ Qt::TopLeftCorner ].yInner( 0.0 );
430
431 break;
432 }
433 case Qt::TopEdge:
434 {
435 orientation = Qt::Horizontal;
436
437 x1 = cn[ Qt::TopLeftCorner ].xInner( 0.0 );
438 x2 = cn[ Qt::TopRightCorner ].xInner( 0.0 );
439 y1 = m_metrics.innerRect.top();
440 y2 = m_metrics.outerRect.top();
441
442 break;
443 }
444 case Qt::BottomEdge:
445 {
446 orientation = Qt::Horizontal;
447
448 x1 = cn[ Qt::BottomRightCorner ].xInner( 0.0 );
449 x2 = cn[ Qt::BottomLeftCorner ].xInner( 0.0 );
450 y1 = m_metrics.innerRect.bottom();
451 y2 = m_metrics.outerRect.bottom();
452
453 break;
454 }
455 case Qt::RightEdge:
456 default:
457 {
458 orientation = Qt::Vertical;
459
460 x1 = m_metrics.innerRect.right();
461 x2 = m_metrics.outerRect.right();
462 y1 = cn[ Qt::TopRightCorner ].yInner( 0.0 );
463 y2 = cn[ Qt::BottomRightCorner ].yInner( 0.0 );
464
465 break;
466 }
467 }
468
469 auto line = lines;
470 const auto& stops = gradient.stops();
471
472 if ( stops.last().position() < 1.0 )
473 setGradientLineAt( orientation, x1, y1, x2, y2, stops.last(), line++ );
474
475 for( int i = stops.count() - 2; i >= 1; i-- )
476 setGradientLineAt( orientation, x1, y1, x2, y2, stops[i], line++ );
477
478 if ( stops.first().position() > 0.0 )
479 setGradientLineAt( orientation, x1, y1, x2, y2, stops.first(), line++ );
480}
481
482void QskBoxBasicStroker::setBorderLines( QskVertex::Line* lines ) const
483{
484 Q_ASSERT( !m_isColored );
485
486 if ( !m_metrics.isOutsideRounded )
487 {
488 const auto& out = m_metrics.outerRect;
489 const auto& in = m_metrics.innerRect;
490
491 lines[0].setLine( in.right(), in.bottom(), out.right(), out.bottom() );
492 lines[1].setLine( in.left(), in.bottom(), out.left(), out.bottom() );
493 lines[2].setLine( in.left(), in.top(), out.left(), out.top() );
494 lines[3].setLine( in.right(), in.top(), out.right(), out.top() );
495 lines[4] = lines[ 0 ];
496
497 return;
498 }
499
500 const auto& gl = m_geometryLayout;
501 const auto cn = m_metrics.corners;
502
503 auto linesTL = lines + gl.cornerOffsets[ Qt::TopLeftCorner ];
504
505 auto linesTR = lines + gl.cornerOffsets[ Qt::TopRightCorner ]
506 + cn[ Qt::TopRightCorner ].stepCount;
507
508 auto linesBL = lines + gl.cornerOffsets[ Qt::BottomLeftCorner ]
509 + cn[ Qt::BottomLeftCorner ].stepCount;
510
511 auto linesBR = lines + gl.cornerOffsets[ Qt::BottomRightCorner ];
512
513 CornerIterator it( m_metrics );
514
515 if ( m_metrics.isOutsideSymmetric && m_metrics.isInsideRounded )
516 {
517 for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
518 {
519 it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
520 it.setBorderLine( Qt::TopRightCorner, linesTR-- );
521 it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
522 it.setBorderLine( Qt::BottomRightCorner, linesBR++ );
523 }
524 }
525 else
526 {
527 for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
528 it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
529
530 for ( it.resetSteps( Qt::TopRightCorner ); !it.isDone(); ++it )
531 it.setBorderLine( Qt::TopRightCorner, linesTR-- );
532
533 for ( it.resetSteps( Qt::BottomLeftCorner ); !it.isDone(); ++it )
534 it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
535
536 for ( it.resetSteps( Qt::BottomRightCorner ); !it.isDone(); ++it )
537 it.setBorderLine( Qt::BottomRightCorner, linesBR++);
538 }
539
540 lines[ gl.closingOffsets[ 1 ] ] = lines[ gl.closingOffsets[ 0 ] ];
541}
542
543void QskBoxBasicStroker::setBorderLines( QskVertex::ColoredLine* lines ) const
544{
545 Q_ASSERT( m_isColored );
546 Q_ASSERT( lines );
547
548 if ( !m_metrics.isOutsideRounded )
549 {
550 qskSetRectBorderLines( m_metrics.innerRect,
551 m_metrics.outerRect, m_borderColors, lines );
552 return;
553 }
554
555 const auto& gl = m_geometryLayout;
556 const auto cn = m_metrics.corners;
557
558 auto linesTL = lines + gl.cornerOffsets[ Qt::TopLeftCorner ];
559
560 auto linesTR = lines + gl.cornerOffsets[ Qt::TopRightCorner ]
561 + cn[ Qt::TopRightCorner ].stepCount;
562
563 auto linesBL = lines + gl.cornerOffsets[ Qt::BottomLeftCorner ]
564 + cn[ Qt::BottomLeftCorner ].stepCount;
565
566 auto linesBR = lines + gl.cornerOffsets[ Qt::BottomRightCorner ];
567
568 CornerIteratorColor it( m_metrics, m_borderColors );
569
570 if ( m_metrics.isOutsideSymmetric && m_metrics.isInsideRounded )
571 {
572 for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
573 {
574 it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
575 it.setBorderLine( Qt::TopRightCorner, linesTR-- );
576 it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
577 it.setBorderLine( Qt::BottomRightCorner, linesBR++ );
578 }
579 }
580 else
581 {
582 for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
583 it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
584
585 for ( it.resetSteps( Qt::TopRightCorner ); !it.isDone(); ++it )
586 it.setBorderLine( Qt::TopRightCorner, linesTR-- );
587
588 for ( it.resetSteps( Qt::BottomLeftCorner ); !it.isDone(); ++it )
589 it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
590
591 for ( it.resetSteps( Qt::BottomRightCorner ); !it.isDone(); ++it )
592 it.setBorderLine( Qt::BottomRightCorner, linesBR++ );
593 }
594
595 setBorderGradientLines( lines );
596 lines[ gl.closingOffsets[ 1 ] ] = lines[ gl.closingOffsets[ 0 ] ];
597}
598
599void QskBoxBasicStroker::setFillLines( QskVertex::Line* lines ) const
600{
601 Q_ASSERT( !m_isColored );
602 Q_ASSERT( lines );
603
604 if ( m_metrics.isInsideRounded )
605 {
606 const LineMap map( m_metrics );
607 qskCreateFill( m_metrics, map, lines );
608 }
609 else // a rectangle
610 {
611 const auto& in = m_metrics.innerRect;
612
613 lines[0].setLine( in.left(), in.top(), in.right(), in.top() );
614 lines[1].setLine( in.left(), in.bottom(), in.right(), in.bottom() );
615 }
616}
617
618void QskBoxBasicStroker::setFillLines( QskVertex::ColoredLine* lines ) const
619{
620 Q_ASSERT( m_isColored );
621 Q_ASSERT( lines );
622 Q_ASSERT( !m_colorMap.isTransparent() );
623
624 if ( m_metrics.isInsideRounded )
625 {
626 const FillMap map( m_metrics, m_colorMap );
627 qskCreateFill( m_metrics, map, lines );
628 }
629 else // a rectangle
630 {
631 const auto& in = m_metrics.innerRect;
632
633 m_colorMap.setLine( in.left(), in.top(), in.right(), in.top(), lines + 0 );
634 m_colorMap.setLine( in.left(), in.bottom(), in.right(), in.bottom(), lines + 1 );
635 }
636}
637
638void QskBoxBasicStroker::setBoxLines( QskVertex::ColoredLine* borderLines,
639 QskVertex::ColoredLine* fillLines ) const
640{
641 Q_ASSERT( m_isColored );
642 Q_ASSERT( borderLines || fillLines );
643 Q_ASSERT( fillLines == nullptr || !m_colorMap.isTransparent() );
644
645 if ( m_metrics.isOutsideSymmetric && m_metrics.isInsideRounded )
646 {
647 if ( borderLines && fillLines )
648 {
649 /*
650 Doing all in one allows a slightly faster implementation.
651 As this is the by far most common situation we do this
652 micro optimization.
653 */
654 setBorderAndFillLines( borderLines, fillLines );
655 return;
656 }
657 }
658
659 if ( borderLines )
660 setBorderLines( borderLines );
661
662 if ( fillLines )
663 setFillLines( fillLines );
664}
665
666void QskBoxBasicStroker::setBorderAndFillLines(
667 QskVertex::ColoredLine* borderLines, QskVertex::ColoredLine* fillLines ) const
668{
669 using namespace Qt;
670
671 const auto& gl = m_geometryLayout;
672
673 const FillMap fillMap( m_metrics, m_colorMap );
674 CornerIteratorColor it( m_metrics, m_borderColors );
675
676 /*
677 It would be possible to run over [0, 0.5 * M_PI_2]
678 and create 8 values ( instead of 4 ) in each step. TODO ...
679 */
680
681 const auto stepCount = m_metrics.corners[0].stepCount;
682
683 auto linesTL = borderLines + gl.cornerOffsets[ TopLeftCorner ];
684 auto linesTR = borderLines + gl.cornerOffsets[ TopRightCorner ] + stepCount;
685 auto linesBL = borderLines + gl.cornerOffsets[ BottomLeftCorner ] + stepCount;
686 auto linesBR = borderLines + gl.cornerOffsets[ BottomRightCorner ];
687
688 if ( m_metrics.preferredOrientation == Qt::Horizontal )
689 {
690 auto l1 = fillLines + stepCount;
691 auto l2 = fillLines + stepCount + 1;
692
693 for ( it.resetSteps( TopLeftCorner ); !it.isDone(); ++it )
694 {
695 it.setBorderLine( TopLeftCorner, linesTL++ );
696 it.setBorderLine( TopRightCorner, linesTR-- );
697 it.setBorderLine( BottomLeftCorner, linesBL-- );
698 it.setBorderLine( BottomRightCorner, linesBR++ );
699
700 fillMap.setVLine( TopLeftCorner, BottomLeftCorner, it.cos(), it.sin(), l1-- );
701 fillMap.setVLine( TopRightCorner, BottomRightCorner, it.cos(), it.sin(), l2++ );
702 }
703 }
704 else
705 {
706 auto l1 = fillLines;
707 auto l2 = fillLines + 2 * stepCount + 1;
708
709 for ( it.resetSteps( TopLeftCorner ); !it.isDone(); ++it )
710 {
711 it.setBorderLine( TopLeftCorner, linesTL++ );
712 it.setBorderLine( TopRightCorner, linesTR-- );
713 it.setBorderLine( BottomLeftCorner, linesBL-- );
714 it.setBorderLine( BottomRightCorner, linesBR++ );
715
716 fillMap.setHLine( TopLeftCorner, TopRightCorner, it.cos(), it.sin(), l1++ );
717 fillMap.setHLine( BottomLeftCorner, BottomRightCorner, it.cos(), it.sin(), l2-- );
718 }
719 }
720
721 if ( borderLines )
722 {
723 setBorderGradientLines( borderLines );
724 borderLines[ gl.closingOffsets[ 1 ] ] = borderLines[ gl.closingOffsets[ 0 ] ];
725 }
726}
727
728int QskBoxBasicStroker::borderCount() const
729{
730 if ( !m_metrics.hasBorder )
731 return 0;
732
733 if ( m_isColored && !m_borderColors.isVisible() )
734 return 0;
735
736 int n = 0;
737
738 if ( m_metrics.isOutsideRounded )
739 {
740 /*
741 4: Number of lines is always one more than the number of steps.
742 1: extra line at the end to close the border path
743 */
744 n = m_metrics.outerStepCount() + 4 + 1;
745
746 if ( m_isColored && !m_borderColors.isMonochrome() )
747 {
748 n += gradientLineCount( m_borderColors.left() );
749 n += gradientLineCount( m_borderColors.top() );
750 n += gradientLineCount( m_borderColors.right() );
751 n += gradientLineCount( m_borderColors.bottom() );
752 }
753 }
754 else
755 {
756 /*
757 4: One for each corner
758 1: extra line at the end to close the border path
759 */
760 n = 4 + 1;
761
762 if ( m_isColored && !m_borderColors.isMonochrome() )
763 {
764 const int gradientLines = -1
765 + m_borderColors.left().stepCount()
766 + m_borderColors.top().stepCount()
767 + m_borderColors.right().stepCount()
768 + m_borderColors.bottom().stepCount();
769
770 n += qMax( gradientLines, 0 );
771 }
772 }
773
774 return n;
775}
776
777int QskBoxBasicStroker::fillCount() const
778{
779 if ( m_metrics.innerRect.isEmpty() )
780 return 0;
781
782 if ( m_isColored && m_colorMap.isTransparent() )
783 return 0;
784
785 int n = 2;
786
787 if ( m_metrics.isInsideRounded )
788 {
789 if ( m_metrics.preferredOrientation == Qt::Horizontal )
790 {
791 n += m_metrics.innerStepCount( Qt::TopLeftCorner, Qt::BottomLeftCorner );
792 n += m_metrics.innerStepCount( Qt::TopRightCorner, Qt::BottomRightCorner );
793 }
794 else
795 {
796 n += m_metrics.innerStepCount( Qt::TopLeftCorner, Qt::TopRightCorner );
797 n += m_metrics.innerStepCount( Qt::BottomLeftCorner, Qt::BottomRightCorner );
798 }
799 }
800
801 return n;
802}