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 auto cn = m_metrics.corners;
252 const bool isHorizontal = m_metrics.preferredOrientation == Qt::Horizontal;
253
254 if ( !m_metrics.isInsideRounded )
255 {
256 map.setHLine( TopLeftCorner, TopRightCorner, 0.0, 1.0, lines );
257 map.setHLine( BottomLeftCorner, BottomRightCorner, 0.0, 1.0, lines + 1 );
258 }
259 else if ( m_metrics.isOutsideSymmetric )
260 {
261 const int stepCount = cn[ 0 ].stepCount;
262
263 if ( isHorizontal )
264 {
265 Line* l1 = lines + stepCount;
266 Line* l2 = lines + stepCount + 1;
267
268 for ( ArcIterator it( stepCount ); !it.isDone(); ++it )
269 {
270 map.setVLine( TopLeftCorner, BottomLeftCorner, it.cos(), it.sin(), l1++ );
271 map.setVLine( TopRightCorner, BottomRightCorner, it.cos(), it.sin(), l2-- );
272 }
273 }
274 else
275 {
276 Line* l1 = lines;
277 Line* l2 = lines + 2 * stepCount + 1;
278
279 for ( ArcIterator it( stepCount ); !it.isDone(); ++it )
280 {
281 map.setHLine( TopLeftCorner, TopRightCorner, it.cos(), it.sin(), l1++ );
282 map.setHLine( BottomLeftCorner, BottomRightCorner, it.cos(), it.sin(), l2-- );
283 }
284 }
285 }
286 else
287 {
288 auto line = lines;
289 int stepCount;
290
291 if ( isHorizontal )
292 {
293 stepCount = m_metrics.innerStepCount( TopLeftCorner, BottomLeftCorner );
294
295 for ( ArcIterator it( stepCount, true ); !it.isDone(); ++it )
296 map.setLine( TopLeftCorner, BottomLeftCorner, it.cos(), it.sin(), line++ );
297
298 stepCount = m_metrics.innerStepCount( TopRightCorner, BottomRightCorner );
299
300 for ( ArcIterator it( stepCount, false ); !it.isDone(); ++it )
301 map.setLine( TopRightCorner, BottomRightCorner, it.cos(), it.sin(), line++ );
302 }
303 else
304 {
305 stepCount = m_metrics.innerStepCount( TopLeftCorner, TopRightCorner );
306
307 for ( ArcIterator it( stepCount, false ); !it.isDone(); ++it )
308 map.setLine( TopLeftCorner, TopRightCorner, it.cos(), it.sin(), line++ );
309
310 stepCount = m_metrics.innerStepCount( BottomLeftCorner, BottomRightCorner );
311
312 for ( ArcIterator it( stepCount, true ); !it.isDone(); ++it )
313 map.setLine( BottomLeftCorner, BottomRightCorner, it.cos(), it.sin(), line++ );
314 }
315 }
316}
317
318QskBoxBasicStroker::GeometryLayout::GeometryLayout(
319 const QskBoxMetrics& metrics, const QskBoxBorderColors& colors )
320{
321 const struct
322 {
323 Qt::Corner corner;
324 Qt::Edge edge;
325 } order[4] =
326 {
327 // counter clockwise
328 { Qt::BottomRightCorner, Qt::RightEdge },
329 { Qt::TopRightCorner, Qt::TopEdge },
330 { Qt::TopLeftCorner, Qt::LeftEdge },
331 { Qt::BottomLeftCorner, Qt::BottomEdge }
332 };
333
334 /*
335 In case of horizontal filling the lines end at right edge,
336 while for vertical filling it is the bottom edge.
337 */
338 const int index0 = ( metrics.preferredOrientation == Qt::Horizontal ) ? 1 : 0;
339
340 int pos = index0;
341
342 for ( int i = 0; i < 4; i++ )
343 {
344 const int idx = ( index0 + i ) % 4;
345
346 const auto corner = order[ idx ].corner;
347 const auto edge = order[ idx ].edge;
348
349 this->cornerOffsets[ corner ] = pos;
350 pos += metrics.corners[ corner ].stepCount + 1;
351
352 this->edgeOffsets[ edgeToIndex( edge ) ] = pos;
353 pos += gradientLineCount( colors.gradientAt( edge ) );
354 }
355
356 if ( index0 == 0 )
357 {
358 this->closingOffsets[ 0 ] = 0;
359 this->closingOffsets[ 1 ] = pos;
360 this->lineCount = pos + 1;
361 }
362 else
363 {
364 pos--;
365
366 this->closingOffsets[ 0 ] = pos;
367 this->closingOffsets[ 1 ] = 0;
368 this->lineCount = pos + 1;
369 }
370}
371
372QskBoxBasicStroker::QskBoxBasicStroker( const QskBoxMetrics& metrics )
373 : m_metrics( metrics )
374 , m_geometryLayout( metrics, QskBoxBorderColors() )
375 , m_isColored( false )
376{
377}
378
379QskBoxBasicStroker::QskBoxBasicStroker( const QskBoxMetrics& metrics,
380 const QskBoxBorderColors& borderColors )
381 : QskBoxBasicStroker( metrics, borderColors, QskVertex::ColorMap() )
382{
383}
384
385QskBoxBasicStroker::QskBoxBasicStroker( const QskBoxMetrics& metrics,
386 const QskBoxBorderColors& borderColors, const QskVertex::ColorMap& colorMap )
387 : m_metrics( metrics )
388 , m_borderColors( borderColors )
389 , m_colorMap( colorMap )
390 , m_geometryLayout( metrics, m_borderColors )
391 , m_isColored( true )
392{
393}
394
395void QskBoxBasicStroker::setBorderGradientLines(
396 QskVertex::ColoredLine* lines ) const
397{
398 const auto off = m_geometryLayout.edgeOffsets;
399
400 setBorderGradientLines( Qt::TopEdge, lines + off[0] );
401 setBorderGradientLines( Qt::LeftEdge, lines + off[1] );
402 setBorderGradientLines( Qt::RightEdge, lines + off[2] );
403 setBorderGradientLines( Qt::BottomEdge, lines + off[3] );
404}
405
406void QskBoxBasicStroker::setBorderGradientLines(
407 Qt::Edge edge, QskVertex::ColoredLine* lines ) const
408{
409 const auto& gradient = m_borderColors.gradientAt( edge );
410 if( gradient.stepCount() <= 1 )
411 {
412 // everything done as contour lines
413 return;
414 }
415
416 const auto cn = m_metrics.corners;
417
418 qreal x1, x2, y1, y2;
419 Qt::Orientation orientation;
420
421 switch( edge )
422 {
423 case Qt::LeftEdge:
424 {
425 orientation = Qt::Vertical;
426
427 x1 = m_metrics.innerRect.left();
428 x2 = m_metrics.outerRect.left();
429 y1 = cn[ Qt::BottomLeftCorner ].yInner( 0.0 );
430 y2 = cn[ Qt::TopLeftCorner ].yInner( 0.0 );
431
432 break;
433 }
434 case Qt::TopEdge:
435 {
436 orientation = Qt::Horizontal;
437
438 x1 = cn[ Qt::TopLeftCorner ].xInner( 0.0 );
439 x2 = cn[ Qt::TopRightCorner ].xInner( 0.0 );
440 y1 = m_metrics.innerRect.top();
441 y2 = m_metrics.outerRect.top();
442
443 break;
444 }
445 case Qt::BottomEdge:
446 {
447 orientation = Qt::Horizontal;
448
449 x1 = cn[ Qt::BottomRightCorner ].xInner( 0.0 );
450 x2 = cn[ Qt::BottomLeftCorner ].xInner( 0.0 );
451 y1 = m_metrics.innerRect.bottom();
452 y2 = m_metrics.outerRect.bottom();
453
454 break;
455 }
456 case Qt::RightEdge:
457 default:
458 {
459 orientation = Qt::Vertical;
460
461 x1 = m_metrics.innerRect.right();
462 x2 = m_metrics.outerRect.right();
463 y1 = cn[ Qt::TopRightCorner ].yInner( 0.0 );
464 y2 = cn[ Qt::BottomRightCorner ].yInner( 0.0 );
465
466 break;
467 }
468 }
469
470 auto line = lines;
471 const auto& stops = gradient.stops();
472
473 if ( stops.last().position() < 1.0 )
474 setGradientLineAt( orientation, x1, y1, x2, y2, stops.last(), line++ );
475
476 for( int i = stops.count() - 2; i >= 1; i-- )
477 setGradientLineAt( orientation, x1, y1, x2, y2, stops[i], line++ );
478
479 if ( stops.first().position() > 0.0 )
480 setGradientLineAt( orientation, x1, y1, x2, y2, stops.first(), line++ );
481}
482
483void QskBoxBasicStroker::setBorderLines( QskVertex::Line* lines ) const
484{
485 Q_ASSERT( !m_isColored );
486
487 if ( !m_metrics.isOutsideRounded )
488 {
489 const auto& out = m_metrics.outerRect;
490 const auto& in = m_metrics.innerRect;
491
492 lines[0].setLine( in.right(), in.bottom(), out.right(), out.bottom() );
493 lines[1].setLine( in.left(), in.bottom(), out.left(), out.bottom() );
494 lines[2].setLine( in.left(), in.top(), out.left(), out.top() );
495 lines[3].setLine( in.right(), in.top(), out.right(), out.top() );
496 lines[4] = lines[ 0 ];
497
498 return;
499 }
500
501 const auto& gl = m_geometryLayout;
502 const auto cn = m_metrics.corners;
503
504 auto linesTL = lines + gl.cornerOffsets[ Qt::TopLeftCorner ];
505
506 auto linesTR = lines + gl.cornerOffsets[ Qt::TopRightCorner ]
507 + cn[ Qt::TopRightCorner ].stepCount;
508
509 auto linesBL = lines + gl.cornerOffsets[ Qt::BottomLeftCorner ]
510 + cn[ Qt::BottomLeftCorner ].stepCount;
511
512 auto linesBR = lines + gl.cornerOffsets[ Qt::BottomRightCorner ];
513
514 CornerIterator it( m_metrics );
515
516 if ( m_metrics.isOutsideSymmetric && m_metrics.isInsideRounded )
517 {
518 for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
519 {
520 it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
521 it.setBorderLine( Qt::TopRightCorner, linesTR-- );
522 it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
523 it.setBorderLine( Qt::BottomRightCorner, linesBR++ );
524 }
525 }
526 else
527 {
528 for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
529 it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
530
531 for ( it.resetSteps( Qt::TopRightCorner ); !it.isDone(); ++it )
532 it.setBorderLine( Qt::TopRightCorner, linesTR-- );
533
534 for ( it.resetSteps( Qt::BottomLeftCorner ); !it.isDone(); ++it )
535 it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
536
537 for ( it.resetSteps( Qt::BottomRightCorner ); !it.isDone(); ++it )
538 it.setBorderLine( Qt::BottomRightCorner, linesBR++);
539 }
540
541 lines[ gl.closingOffsets[ 1 ] ] = lines[ gl.closingOffsets[ 0 ] ];
542}
543
544void QskBoxBasicStroker::setBorderLines( QskVertex::ColoredLine* lines ) const
545{
546 Q_ASSERT( m_isColored );
547 Q_ASSERT( lines );
548
549 if ( !m_metrics.isOutsideRounded )
550 {
551 qskSetRectBorderLines( m_metrics.innerRect,
552 m_metrics.outerRect, m_borderColors, lines );
553 return;
554 }
555
556 const auto& gl = m_geometryLayout;
557 const auto cn = m_metrics.corners;
558
559 auto linesTL = lines + gl.cornerOffsets[ Qt::TopLeftCorner ];
560
561 auto linesTR = lines + gl.cornerOffsets[ Qt::TopRightCorner ]
562 + cn[ Qt::TopRightCorner ].stepCount;
563
564 auto linesBL = lines + gl.cornerOffsets[ Qt::BottomLeftCorner ]
565 + cn[ Qt::BottomLeftCorner ].stepCount;
566
567 auto linesBR = lines + gl.cornerOffsets[ Qt::BottomRightCorner ];
568
569 CornerIteratorColor it( m_metrics, m_borderColors );
570
571 if ( m_metrics.isOutsideSymmetric && m_metrics.isInsideRounded )
572 {
573 for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
574 {
575 it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
576 it.setBorderLine( Qt::TopRightCorner, linesTR-- );
577 it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
578 it.setBorderLine( Qt::BottomRightCorner, linesBR++ );
579 }
580 }
581 else
582 {
583 for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
584 it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
585
586 for ( it.resetSteps( Qt::TopRightCorner ); !it.isDone(); ++it )
587 it.setBorderLine( Qt::TopRightCorner, linesTR-- );
588
589 for ( it.resetSteps( Qt::BottomLeftCorner ); !it.isDone(); ++it )
590 it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
591
592 for ( it.resetSteps( Qt::BottomRightCorner ); !it.isDone(); ++it )
593 it.setBorderLine( Qt::BottomRightCorner, linesBR++ );
594 }
595
596 setBorderGradientLines( lines );
597 lines[ gl.closingOffsets[ 1 ] ] = lines[ gl.closingOffsets[ 0 ] ];
598}
599
600void QskBoxBasicStroker::setFillLines( QskVertex::Line* lines ) const
601{
602 Q_ASSERT( !m_isColored );
603 Q_ASSERT( lines );
604
605 if ( m_metrics.isInsideRounded )
606 {
607 const LineMap map( m_metrics );
608 qskCreateFill( m_metrics, map, lines );
609 }
610 else // a rectangle
611 {
612 const auto& in = m_metrics.innerRect;
613
614 lines[0].setLine( in.left(), in.top(), in.right(), in.top() );
615 lines[1].setLine( in.left(), in.bottom(), in.right(), in.bottom() );
616 }
617}
618
619void QskBoxBasicStroker::setFillLines( QskVertex::ColoredLine* lines ) const
620{
621 Q_ASSERT( m_isColored );
622 Q_ASSERT( lines );
623 Q_ASSERT( !m_colorMap.isTransparent() );
624
625 if ( m_metrics.isInsideRounded )
626 {
627 const FillMap map( m_metrics, m_colorMap );
628 qskCreateFill( m_metrics, map, lines );
629 }
630 else // a rectangle
631 {
632 const auto& in = m_metrics.innerRect;
633
634 m_colorMap.setLine( in.left(), in.top(), in.right(), in.top(), lines + 0 );
635 m_colorMap.setLine( in.left(), in.bottom(), in.right(), in.bottom(), lines + 1 );
636 }
637}
638
639void QskBoxBasicStroker::setBoxLines( QskVertex::ColoredLine* borderLines,
640 QskVertex::ColoredLine* fillLines ) const
641{
642 Q_ASSERT( m_isColored );
643 Q_ASSERT( borderLines || fillLines );
644 Q_ASSERT( fillLines == nullptr || !m_colorMap.isTransparent() );
645
646 if ( m_metrics.isOutsideSymmetric && m_metrics.isInsideRounded )
647 {
648 if ( borderLines && fillLines )
649 {
650 /*
651 Doing all in one allows a slightly faster implementation.
652 As this is the by far most common situation we do this
653 micro optimization.
654 */
655 setBorderAndFillLines( borderLines, fillLines );
656 return;
657 }
658 }
659
660 if ( borderLines )
661 setBorderLines( borderLines );
662
663 if ( fillLines )
664 setFillLines( fillLines );
665}
666
667void QskBoxBasicStroker::setBorderAndFillLines(
668 QskVertex::ColoredLine* borderLines, QskVertex::ColoredLine* fillLines ) const
669{
670 using namespace Qt;
671
672 const auto& gl = m_geometryLayout;
673
674 const FillMap fillMap( m_metrics, m_colorMap );
675 CornerIteratorColor it( m_metrics, m_borderColors );
676
677 /*
678 It would be possible to run over [0, 0.5 * M_PI_2]
679 and create 8 values ( instead of 4 ) in each step. TODO ...
680 */
681
682 const auto stepCount = m_metrics.corners[0].stepCount;
683
684 auto linesTL = borderLines + gl.cornerOffsets[ TopLeftCorner ];
685 auto linesTR = borderLines + gl.cornerOffsets[ TopRightCorner ] + stepCount;
686 auto linesBL = borderLines + gl.cornerOffsets[ BottomLeftCorner ] + stepCount;
687 auto linesBR = borderLines + gl.cornerOffsets[ BottomRightCorner ];
688
689 if ( m_metrics.preferredOrientation == Qt::Horizontal )
690 {
691 auto l1 = fillLines + stepCount;
692 auto l2 = fillLines + stepCount + 1;
693
694 for ( it.resetSteps( TopLeftCorner ); !it.isDone(); ++it )
695 {
696 it.setBorderLine( TopLeftCorner, linesTL++ );
697 it.setBorderLine( TopRightCorner, linesTR-- );
698 it.setBorderLine( BottomLeftCorner, linesBL-- );
699 it.setBorderLine( BottomRightCorner, linesBR++ );
700
701 fillMap.setVLine( TopLeftCorner, BottomLeftCorner, it.cos(), it.sin(), l1-- );
702 fillMap.setVLine( TopRightCorner, BottomRightCorner, it.cos(), it.sin(), l2++ );
703 }
704 }
705 else
706 {
707 auto l1 = fillLines;
708 auto l2 = fillLines + 2 * stepCount + 1;
709
710 for ( it.resetSteps( TopLeftCorner ); !it.isDone(); ++it )
711 {
712 it.setBorderLine( TopLeftCorner, linesTL++ );
713 it.setBorderLine( TopRightCorner, linesTR-- );
714 it.setBorderLine( BottomLeftCorner, linesBL-- );
715 it.setBorderLine( BottomRightCorner, linesBR++ );
716
717 fillMap.setHLine( TopLeftCorner, TopRightCorner, it.cos(), it.sin(), l1++ );
718 fillMap.setHLine( BottomLeftCorner, BottomRightCorner, it.cos(), it.sin(), l2-- );
719 }
720 }
721
722 if ( borderLines )
723 {
724 setBorderGradientLines( borderLines );
725 borderLines[ gl.closingOffsets[ 1 ] ] = borderLines[ gl.closingOffsets[ 0 ] ];
726 }
727}
728
729int QskBoxBasicStroker::borderCount() const
730{
731 if ( !m_metrics.hasBorder )
732 return 0;
733
734 if ( m_isColored && !m_borderColors.isVisible() )
735 return 0;
736
737 int n = 0;
738
739 if ( m_metrics.isOutsideRounded )
740 {
741 /*
742 4: Number of lines is always one more than the number of steps.
743 1: extra line at the end to close the border path
744 */
745 n = m_metrics.outerStepCount() + 4 + 1;
746
747 if ( m_isColored && !m_borderColors.isMonochrome() )
748 {
749 n += gradientLineCount( m_borderColors.left() );
750 n += gradientLineCount( m_borderColors.top() );
751 n += gradientLineCount( m_borderColors.right() );
752 n += gradientLineCount( m_borderColors.bottom() );
753 }
754 }
755 else
756 {
757 /*
758 4: One for each corner
759 1: extra line at the end to close the border path
760 */
761 n = 4 + 1;
762
763 if ( m_isColored && !m_borderColors.isMonochrome() )
764 {
765 const int gradientLines = -1
766 + m_borderColors.left().stepCount()
767 + m_borderColors.top().stepCount()
768 + m_borderColors.right().stepCount()
769 + m_borderColors.bottom().stepCount();
770
771 n += qMax( gradientLines, 0 );
772 }
773 }
774
775 return n;
776}
777
778int QskBoxBasicStroker::fillCount() const
779{
780 if ( m_metrics.innerRect.isEmpty() )
781 return 0;
782
783 if ( m_isColored && m_colorMap.isTransparent() )
784 return 0;
785
786 int n = 2;
787
788 if ( m_metrics.isInsideRounded )
789 {
790 if ( m_metrics.preferredOrientation == Qt::Horizontal )
791 {
792 n += m_metrics.innerStepCount( Qt::TopLeftCorner, Qt::BottomLeftCorner );
793 n += m_metrics.innerStepCount( Qt::TopRightCorner, Qt::BottomRightCorner );
794 }
795 else
796 {
797 n += m_metrics.innerStepCount( Qt::TopLeftCorner, Qt::TopRightCorner );
798 n += m_metrics.innerStepCount( Qt::BottomLeftCorner, Qt::BottomRightCorner );
799 }
800 }
801
802 return n;
803}