6#include "QskBoxBasicStroker.h"
10 inline int gradientLineCount(
const QskGradient& borderGradient )
13 return qMax( 0, borderGradient.stepCount() - 1 );
16 inline void setGradientLineAt(
17 Qt::Orientation orientation, qreal x1, qreal y1, qreal x2, qreal y2,
20 if ( orientation == Qt::Horizontal )
22 const auto pos = x1 + stop.position() * ( x2 - x1 );
23 line->setLine( pos, y1, pos, y2, stop.rgb() );
27 const auto pos = y1 + stop.position() * ( y2 - y1 );
28 line->setLine( x1, pos, x2, pos, stop.rgb() );
32 inline int edgeToIndex( Qt::Edge edge )
33 {
return qCountTrailingZeroBits(
static_cast< quint8
>( edge ) ); }
39 : m_corners( metrics.corners )
43 inline void resetSteps(
int corner,
bool inverted =
false )
45 reset( m_corners[ corner ].stepCount, inverted );
50 const auto& c = m_corners[ corner ];
52 line->setLine( c.xInner( cos() ), c.yInner( sin() ),
53 c.xOuter( cos() ), c.yOuter( sin() ) );
60 class CornerIteratorColor :
public CornerIterator
65 : CornerIterator( metrics )
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() } }
76 const auto& c = m_corners[ corner ];
78 line->setLine( c.xInner( cos() ), c.yInner( sin() ),
79 c.xOuter( cos() ), c.yOuter( sin() ), color( corner ) );
85 const auto& cs = m_colors[ corner ];
87 if ( cs.first == cs.second )
90 const auto ratio = step() / qreal( m_corners[ corner ].stepCount );
91 return cs.first.interpolatedTo( cs.second, ratio );
94 const QPair< QskVertex::Color, QskVertex::Color > m_colors[4];
101 : m_corners( metrics.corners )
105 inline void setHLine(
int corner1,
int corner2,
108 const qreal y = m_corners[ corner1 ].yInner( sin );
110 const qreal x1 = m_corners[ corner1 ].xInner( cos );
111 const qreal x2 = m_corners[ corner2 ].xInner( cos );
113 line->setLine( x1, y, x2, y );
116 inline void setVLine(
int corner1,
int corner2,
119 const qreal x = m_corners[ corner1 ].xInner( cos );
121 const qreal y1 = m_corners[ corner1 ].yInner( sin );
122 const qreal y2 = m_corners[ corner2 ].yInner( sin );
124 line->setLine( x, y1, x, y2 );
127 inline void setLine(
int corner1,
int corner2,
130 const qreal x1 = m_corners[ corner1 ].xInner( cos );
131 const qreal x2 = m_corners[ corner2 ].xInner( cos );
133 const qreal y1 = m_corners[ corner1 ].yInner( sin );
134 const qreal y2 = m_corners[ corner2 ].yInner( sin );
136 line->setLine( x1, y1, x2, y2 );
146 const QskVertex::ColorMap& colorMap )
147 : m_colorMap( colorMap )
148 , m_corners( metrics.corners )
152 inline void setHLine(
int corner1,
int corner2,
155 const qreal y = m_corners[ corner1 ].yInner( sin );
157 const qreal x1 = m_corners[ corner1 ].xInner( cos );
158 const qreal x2 = m_corners[ corner2 ].xInner( cos );
160 m_colorMap.setLine( x1, y, x2, y, line );
163 inline void setVLine(
int corner1,
int corner2,
166 const qreal x = m_corners[ corner1 ].xInner( cos );
168 const qreal y1 = m_corners[ corner1 ].yInner( sin );
169 const qreal y2 = m_corners[ corner2 ].yInner( sin );
171 m_colorMap.setLine( x, y1, x, y2, line );
174 inline void setLine(
int corner1,
int corner2,
177 const qreal x1 = m_corners[ corner1 ].xInner( cos );
178 const qreal x2 = m_corners[ corner2 ].xInner( cos );
180 const qreal y1 = m_corners[ corner1 ].yInner( sin );
181 const qreal y2 = m_corners[ corner2 ].yInner( sin );
183 m_colorMap.setLine( x1, y1, x2, y2, line );
186 const QskVertex::ColorMap& m_colorMap;
192 const QLineF& l1,
const QLineF& l2,
const QskGradient& gradient,
195 const auto stops = gradient.stops();
197 if ( stops.first().position() > 0.0 )
198 ( lines++ )->setLine( l1, stops.first().rgb() );
200 for(
const auto& stop : stops )
202 const auto p1 = l1.p1() + stop.position() * ( l2.p1() - l1.p1() );
203 const auto p2 = l1.p2() + stop.position() * ( l2.p2() - l1.p2() );
205 ( lines++ )->setLine( p1, p2, stop.rgb() );
208 if ( stops.last().position() < 1.0 )
209 ( lines++ )->setLine( l2, stops.last().rgb() );
214inline void qskSetRectBorderLines(
const QRectF& in,
const QRectF& out,
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() }
225 if ( colors.isMonochrome() )
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 ];
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 );
244template<
class Line,
class FillMap >
245static inline void qskCreateFill(
246 const QskBoxMetrics& m_metrics,
const FillMap& map, Line* lines )
248 using namespace QskVertex;
251 const bool isHorizontal = ( m_metrics.preferredOrientation == Qt::Horizontal );
253 if ( !m_metrics.isInsideRounded )
255 map.setHLine( TopLeftCorner, TopRightCorner, 0.0, 1.0, lines );
256 map.setHLine( BottomLeftCorner, BottomRightCorner, 0.0, 1.0, lines + 1 );
258 else if ( m_metrics.isOutsideSymmetric )
260 const int stepCount = m_metrics.corners[ 0 ].innerStepCount();
264 Line* l1 = lines + stepCount;
265 Line* l2 = lines + stepCount + 1;
267 for (
ArcIterator it( stepCount ); !it.isDone(); ++it )
269 map.setVLine( TopLeftCorner, BottomLeftCorner, it.cos(), it.sin(), l1++ );
270 map.setVLine( TopRightCorner, BottomRightCorner, it.cos(), it.sin(), l2-- );
276 Line* l2 = lines + 2 * stepCount + 1;
278 for (
ArcIterator it( stepCount ); !it.isDone(); ++it )
280 map.setHLine( TopLeftCorner, TopRightCorner, it.cos(), it.sin(), l1++ );
281 map.setHLine( BottomLeftCorner, BottomRightCorner, it.cos(), it.sin(), l2-- );
292 stepCount = m_metrics.innerStepCount( TopLeftCorner, BottomLeftCorner );
294 for (
ArcIterator it( stepCount,
true ); !it.isDone(); ++it )
295 map.setLine( TopLeftCorner, BottomLeftCorner, it.cos(), it.sin(), line++ );
297 stepCount = m_metrics.innerStepCount( TopRightCorner, BottomRightCorner );
299 for (
ArcIterator it( stepCount,
false ); !it.isDone(); ++it )
300 map.setLine( TopRightCorner, BottomRightCorner, it.cos(), it.sin(), line++ );
304 stepCount = m_metrics.innerStepCount( TopLeftCorner, TopRightCorner );
306 for (
ArcIterator it( stepCount,
false ); !it.isDone(); ++it )
307 map.setLine( TopLeftCorner, TopRightCorner, it.cos(), it.sin(), line++ );
309 stepCount = m_metrics.innerStepCount( BottomLeftCorner, BottomRightCorner );
311 for (
ArcIterator it( stepCount,
true ); !it.isDone(); ++it )
312 map.setLine( BottomLeftCorner, BottomRightCorner, it.cos(), it.sin(), line++ );
317QskBoxBasicStroker::GeometryLayout::GeometryLayout(
327 { Qt::BottomRightCorner, Qt::RightEdge },
328 { Qt::TopRightCorner, Qt::TopEdge },
329 { Qt::TopLeftCorner, Qt::LeftEdge },
330 { Qt::BottomLeftCorner, Qt::BottomEdge }
337 const int index0 = ( metrics.preferredOrientation == Qt::Horizontal ) ? 1 : 0;
341 for (
int i = 0; i < 4; i++ )
343 const int idx = ( index0 + i ) % 4;
345 const auto corner = order[ idx ].corner;
346 const auto edge = order[ idx ].edge;
348 this->cornerOffsets[ corner ] = pos;
349 pos += metrics.corners[ corner ].stepCount + 1;
351 this->edgeOffsets[ edgeToIndex( edge ) ] = pos;
352 pos += gradientLineCount( colors.gradientAt( edge ) );
357 this->closingOffsets[ 0 ] = 0;
358 this->closingOffsets[ 1 ] = pos;
359 this->lineCount = pos + 1;
365 this->closingOffsets[ 0 ] = pos;
366 this->closingOffsets[ 1 ] = 0;
367 this->lineCount = pos + 1;
371QskBoxBasicStroker::QskBoxBasicStroker(
const QskBoxMetrics& metrics )
372 : m_metrics( metrics )
374 , m_isColored( false )
378QskBoxBasicStroker::QskBoxBasicStroker(
const QskBoxMetrics& metrics,
384QskBoxBasicStroker::QskBoxBasicStroker(
const QskBoxMetrics& metrics,
386 : m_metrics( metrics )
387 , m_borderColors( borderColors )
388 , m_colorMap( colorMap )
389 , m_geometryLayout( metrics, m_borderColors )
390 , m_isColored( true )
394void QskBoxBasicStroker::setBorderGradientLines(
397 const auto off = m_geometryLayout.edgeOffsets;
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] );
405void QskBoxBasicStroker::setBorderGradientLines(
408 const auto& gradient = m_borderColors.gradientAt( edge );
409 if( gradient.stepCount() <= 1 )
415 const auto cn = m_metrics.corners;
417 qreal x1, x2, y1, y2;
418 Qt::Orientation orientation;
424 orientation = Qt::Vertical;
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 );
435 orientation = Qt::Horizontal;
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();
446 orientation = Qt::Horizontal;
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();
458 orientation = Qt::Vertical;
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 );
470 const auto& stops = gradient.stops();
472 if ( stops.last().position() < 1.0 )
473 setGradientLineAt( orientation, x1, y1, x2, y2, stops.last(), line++ );
475 for(
int i = stops.count() - 2; i >= 1; i-- )
476 setGradientLineAt( orientation, x1, y1, x2, y2, stops[i], line++ );
478 if ( stops.first().position() > 0.0 )
479 setGradientLineAt( orientation, x1, y1, x2, y2, stops.first(), line++ );
484 Q_ASSERT( !m_isColored );
486 if ( !m_metrics.isOutsideRounded )
488 const auto& out = m_metrics.outerRect;
489 const auto& in = m_metrics.innerRect;
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 ];
500 const auto& gl = m_geometryLayout;
501 const auto cn = m_metrics.corners;
503 auto linesTL = lines + gl.cornerOffsets[ Qt::TopLeftCorner ];
505 auto linesTR = lines + gl.cornerOffsets[ Qt::TopRightCorner ]
506 + cn[ Qt::TopRightCorner ].stepCount;
508 auto linesBL = lines + gl.cornerOffsets[ Qt::BottomLeftCorner ]
509 + cn[ Qt::BottomLeftCorner ].stepCount;
511 auto linesBR = lines + gl.cornerOffsets[ Qt::BottomRightCorner ];
513 CornerIterator it( m_metrics );
515 if ( m_metrics.isOutsideSymmetric && m_metrics.isInsideRounded )
517 for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
519 it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
520 it.setBorderLine( Qt::TopRightCorner, linesTR-- );
521 it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
522 it.setBorderLine( Qt::BottomRightCorner, linesBR++ );
527 for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
528 it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
530 for ( it.resetSteps( Qt::TopRightCorner ); !it.isDone(); ++it )
531 it.setBorderLine( Qt::TopRightCorner, linesTR-- );
533 for ( it.resetSteps( Qt::BottomLeftCorner ); !it.isDone(); ++it )
534 it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
536 for ( it.resetSteps( Qt::BottomRightCorner ); !it.isDone(); ++it )
537 it.setBorderLine( Qt::BottomRightCorner, linesBR++);
540 lines[ gl.closingOffsets[ 1 ] ] = lines[ gl.closingOffsets[ 0 ] ];
545 Q_ASSERT( m_isColored );
548 if ( !m_metrics.isOutsideRounded )
550 qskSetRectBorderLines( m_metrics.innerRect,
551 m_metrics.outerRect, m_borderColors, lines );
555 const auto& gl = m_geometryLayout;
556 const auto cn = m_metrics.corners;
558 auto linesTL = lines + gl.cornerOffsets[ Qt::TopLeftCorner ];
560 auto linesTR = lines + gl.cornerOffsets[ Qt::TopRightCorner ]
561 + cn[ Qt::TopRightCorner ].stepCount;
563 auto linesBL = lines + gl.cornerOffsets[ Qt::BottomLeftCorner ]
564 + cn[ Qt::BottomLeftCorner ].stepCount;
566 auto linesBR = lines + gl.cornerOffsets[ Qt::BottomRightCorner ];
568 CornerIteratorColor it( m_metrics, m_borderColors );
570 if ( m_metrics.isOutsideSymmetric && m_metrics.isInsideRounded )
572 for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
574 it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
575 it.setBorderLine( Qt::TopRightCorner, linesTR-- );
576 it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
577 it.setBorderLine( Qt::BottomRightCorner, linesBR++ );
582 for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
583 it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
585 for ( it.resetSteps( Qt::TopRightCorner ); !it.isDone(); ++it )
586 it.setBorderLine( Qt::TopRightCorner, linesTR-- );
588 for ( it.resetSteps( Qt::BottomLeftCorner ); !it.isDone(); ++it )
589 it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
591 for ( it.resetSteps( Qt::BottomRightCorner ); !it.isDone(); ++it )
592 it.setBorderLine( Qt::BottomRightCorner, linesBR++ );
595 setBorderGradientLines( lines );
596 lines[ gl.closingOffsets[ 1 ] ] = lines[ gl.closingOffsets[ 0 ] ];
601 Q_ASSERT( !m_isColored );
604 if ( m_metrics.isInsideRounded )
606 const LineMap map( m_metrics );
607 qskCreateFill( m_metrics, map, lines );
611 const auto& in = m_metrics.innerRect;
613 lines[0].setLine( in.left(), in.top(), in.right(), in.top() );
614 lines[1].setLine( in.left(), in.bottom(), in.right(), in.bottom() );
620 Q_ASSERT( m_isColored );
622 Q_ASSERT( !m_colorMap.isTransparent() );
624 if ( m_metrics.isInsideRounded )
626 const FillMap map( m_metrics, m_colorMap );
627 qskCreateFill( m_metrics, map, lines );
631 const auto& in = m_metrics.innerRect;
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 );
641 Q_ASSERT( m_isColored );
642 Q_ASSERT( borderLines || fillLines );
643 Q_ASSERT( fillLines ==
nullptr || !m_colorMap.isTransparent() );
645 if ( m_metrics.isOutsideSymmetric && m_metrics.isInsideRounded )
647 if ( borderLines && fillLines )
654 setBorderAndFillLines( borderLines, fillLines );
660 setBorderLines( borderLines );
663 setFillLines( fillLines );
666void QskBoxBasicStroker::setBorderAndFillLines(
671 const auto& gl = m_geometryLayout;
673 const FillMap fillMap( m_metrics, m_colorMap );
674 CornerIteratorColor it( m_metrics, m_borderColors );
681 const auto stepCount = m_metrics.corners[0].stepCount;
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 ];
688 if ( m_metrics.preferredOrientation == Qt::Horizontal )
690 auto l1 = fillLines + stepCount;
691 auto l2 = fillLines + stepCount + 1;
693 for ( it.resetSteps( TopLeftCorner ); !it.isDone(); ++it )
695 it.setBorderLine( TopLeftCorner, linesTL++ );
696 it.setBorderLine( TopRightCorner, linesTR-- );
697 it.setBorderLine( BottomLeftCorner, linesBL-- );
698 it.setBorderLine( BottomRightCorner, linesBR++ );
700 fillMap.setVLine( TopLeftCorner, BottomLeftCorner, it.cos(), it.sin(), l1-- );
701 fillMap.setVLine( TopRightCorner, BottomRightCorner, it.cos(), it.sin(), l2++ );
707 auto l2 = fillLines + 2 * stepCount + 1;
709 for ( it.resetSteps( TopLeftCorner ); !it.isDone(); ++it )
711 it.setBorderLine( TopLeftCorner, linesTL++ );
712 it.setBorderLine( TopRightCorner, linesTR-- );
713 it.setBorderLine( BottomLeftCorner, linesBL-- );
714 it.setBorderLine( BottomRightCorner, linesBR++ );
716 fillMap.setHLine( TopLeftCorner, TopRightCorner, it.cos(), it.sin(), l1++ );
717 fillMap.setHLine( BottomLeftCorner, BottomRightCorner, it.cos(), it.sin(), l2-- );
723 setBorderGradientLines( borderLines );
724 borderLines[ gl.closingOffsets[ 1 ] ] = borderLines[ gl.closingOffsets[ 0 ] ];
728int QskBoxBasicStroker::borderCount()
const
730 if ( !m_metrics.hasBorder )
733 if ( m_isColored && !m_borderColors.isVisible() )
738 if ( m_metrics.isOutsideRounded )
744 n = m_metrics.outerStepCount() + 4 + 1;
746 if ( m_isColored && !m_borderColors.isMonochrome() )
748 n += gradientLineCount( m_borderColors.left() );
749 n += gradientLineCount( m_borderColors.top() );
750 n += gradientLineCount( m_borderColors.right() );
751 n += gradientLineCount( m_borderColors.bottom() );
762 if ( m_isColored && !m_borderColors.isMonochrome() )
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();
770 n += qMax( gradientLines, 0 );
777int QskBoxBasicStroker::fillCount()
const
779 if ( m_metrics.innerRect.isEmpty() )
782 if ( m_isColored && m_colorMap.isTransparent() )
787 if ( m_metrics.isInsideRounded )
789 if ( m_metrics.preferredOrientation == Qt::Horizontal )
791 n += m_metrics.innerStepCount( Qt::TopLeftCorner, Qt::BottomLeftCorner );
792 n += m_metrics.innerStepCount( Qt::TopRightCorner, Qt::BottomRightCorner );
796 n += m_metrics.innerStepCount( Qt::TopLeftCorner, Qt::TopRightCorner );
797 n += m_metrics.innerStepCount( Qt::BottomLeftCorner, Qt::BottomRightCorner );