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 auto cn = m_metrics.corners;
252 const bool isHorizontal = m_metrics.preferredOrientation == Qt::Horizontal;
254 if ( !m_metrics.isInsideRounded )
256 map.setHLine( TopLeftCorner, TopRightCorner, 0.0, 1.0, lines );
257 map.setHLine( BottomLeftCorner, BottomRightCorner, 0.0, 1.0, lines + 1 );
259 else if ( m_metrics.isOutsideSymmetric )
261 const int stepCount = cn[ 0 ].stepCount;
265 Line* l1 = lines + stepCount;
266 Line* l2 = lines + stepCount + 1;
268 for (
ArcIterator it( stepCount ); !it.isDone(); ++it )
270 map.setVLine( TopLeftCorner, BottomLeftCorner, it.cos(), it.sin(), l1++ );
271 map.setVLine( TopRightCorner, BottomRightCorner, it.cos(), it.sin(), l2-- );
277 Line* l2 = lines + 2 * stepCount + 1;
279 for (
ArcIterator it( stepCount ); !it.isDone(); ++it )
281 map.setHLine( TopLeftCorner, TopRightCorner, it.cos(), it.sin(), l1++ );
282 map.setHLine( BottomLeftCorner, BottomRightCorner, it.cos(), it.sin(), l2-- );
293 stepCount = m_metrics.innerStepCount( TopLeftCorner, BottomLeftCorner );
295 for (
ArcIterator it( stepCount,
true ); !it.isDone(); ++it )
296 map.setLine( TopLeftCorner, BottomLeftCorner, it.cos(), it.sin(), line++ );
298 stepCount = m_metrics.innerStepCount( TopRightCorner, BottomRightCorner );
300 for (
ArcIterator it( stepCount,
false ); !it.isDone(); ++it )
301 map.setLine( TopRightCorner, BottomRightCorner, it.cos(), it.sin(), line++ );
305 stepCount = m_metrics.innerStepCount( TopLeftCorner, TopRightCorner );
307 for (
ArcIterator it( stepCount,
false ); !it.isDone(); ++it )
308 map.setLine( TopLeftCorner, TopRightCorner, it.cos(), it.sin(), line++ );
310 stepCount = m_metrics.innerStepCount( BottomLeftCorner, BottomRightCorner );
312 for (
ArcIterator it( stepCount,
true ); !it.isDone(); ++it )
313 map.setLine( BottomLeftCorner, BottomRightCorner, it.cos(), it.sin(), line++ );
318QskBoxBasicStroker::GeometryLayout::GeometryLayout(
328 { Qt::BottomRightCorner, Qt::RightEdge },
329 { Qt::TopRightCorner, Qt::TopEdge },
330 { Qt::TopLeftCorner, Qt::LeftEdge },
331 { Qt::BottomLeftCorner, Qt::BottomEdge }
338 const int index0 = ( metrics.preferredOrientation == Qt::Horizontal ) ? 1 : 0;
342 for (
int i = 0; i < 4; i++ )
344 const int idx = ( index0 + i ) % 4;
346 const auto corner = order[ idx ].corner;
347 const auto edge = order[ idx ].edge;
349 this->cornerOffsets[ corner ] = pos;
350 pos += metrics.corners[ corner ].stepCount + 1;
352 this->edgeOffsets[ edgeToIndex( edge ) ] = pos;
353 pos += gradientLineCount( colors.gradientAt( edge ) );
358 this->closingOffsets[ 0 ] = 0;
359 this->closingOffsets[ 1 ] = pos;
360 this->lineCount = pos + 1;
366 this->closingOffsets[ 0 ] = pos;
367 this->closingOffsets[ 1 ] = 0;
368 this->lineCount = pos + 1;
372QskBoxBasicStroker::QskBoxBasicStroker(
const QskBoxMetrics& metrics )
373 : m_metrics( metrics )
375 , m_isColored( false )
379QskBoxBasicStroker::QskBoxBasicStroker(
const QskBoxMetrics& metrics,
385QskBoxBasicStroker::QskBoxBasicStroker(
const QskBoxMetrics& metrics,
387 : m_metrics( metrics )
388 , m_borderColors( borderColors )
389 , m_colorMap( colorMap )
390 , m_geometryLayout( metrics, m_borderColors )
391 , m_isColored( true )
395void QskBoxBasicStroker::setBorderGradientLines(
398 const auto off = m_geometryLayout.edgeOffsets;
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] );
406void QskBoxBasicStroker::setBorderGradientLines(
409 const auto& gradient = m_borderColors.gradientAt( edge );
410 if( gradient.stepCount() <= 1 )
416 const auto cn = m_metrics.corners;
418 qreal x1, x2, y1, y2;
419 Qt::Orientation orientation;
425 orientation = Qt::Vertical;
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 );
436 orientation = Qt::Horizontal;
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();
447 orientation = Qt::Horizontal;
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();
459 orientation = Qt::Vertical;
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 );
471 const auto& stops = gradient.stops();
473 if ( stops.last().position() < 1.0 )
474 setGradientLineAt( orientation, x1, y1, x2, y2, stops.last(), line++ );
476 for(
int i = stops.count() - 2; i >= 1; i-- )
477 setGradientLineAt( orientation, x1, y1, x2, y2, stops[i], line++ );
479 if ( stops.first().position() > 0.0 )
480 setGradientLineAt( orientation, x1, y1, x2, y2, stops.first(), line++ );
485 Q_ASSERT( !m_isColored );
487 if ( !m_metrics.isOutsideRounded )
489 const auto& out = m_metrics.outerRect;
490 const auto& in = m_metrics.innerRect;
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 ];
501 const auto& gl = m_geometryLayout;
502 const auto cn = m_metrics.corners;
504 auto linesTL = lines + gl.cornerOffsets[ Qt::TopLeftCorner ];
506 auto linesTR = lines + gl.cornerOffsets[ Qt::TopRightCorner ]
507 + cn[ Qt::TopRightCorner ].stepCount;
509 auto linesBL = lines + gl.cornerOffsets[ Qt::BottomLeftCorner ]
510 + cn[ Qt::BottomLeftCorner ].stepCount;
512 auto linesBR = lines + gl.cornerOffsets[ Qt::BottomRightCorner ];
514 CornerIterator it( m_metrics );
516 if ( m_metrics.isOutsideSymmetric && m_metrics.isInsideRounded )
518 for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
520 it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
521 it.setBorderLine( Qt::TopRightCorner, linesTR-- );
522 it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
523 it.setBorderLine( Qt::BottomRightCorner, linesBR++ );
528 for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
529 it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
531 for ( it.resetSteps( Qt::TopRightCorner ); !it.isDone(); ++it )
532 it.setBorderLine( Qt::TopRightCorner, linesTR-- );
534 for ( it.resetSteps( Qt::BottomLeftCorner ); !it.isDone(); ++it )
535 it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
537 for ( it.resetSteps( Qt::BottomRightCorner ); !it.isDone(); ++it )
538 it.setBorderLine( Qt::BottomRightCorner, linesBR++);
541 lines[ gl.closingOffsets[ 1 ] ] = lines[ gl.closingOffsets[ 0 ] ];
546 Q_ASSERT( m_isColored );
549 if ( !m_metrics.isOutsideRounded )
551 qskSetRectBorderLines( m_metrics.innerRect,
552 m_metrics.outerRect, m_borderColors, lines );
556 const auto& gl = m_geometryLayout;
557 const auto cn = m_metrics.corners;
559 auto linesTL = lines + gl.cornerOffsets[ Qt::TopLeftCorner ];
561 auto linesTR = lines + gl.cornerOffsets[ Qt::TopRightCorner ]
562 + cn[ Qt::TopRightCorner ].stepCount;
564 auto linesBL = lines + gl.cornerOffsets[ Qt::BottomLeftCorner ]
565 + cn[ Qt::BottomLeftCorner ].stepCount;
567 auto linesBR = lines + gl.cornerOffsets[ Qt::BottomRightCorner ];
569 CornerIteratorColor it( m_metrics, m_borderColors );
571 if ( m_metrics.isOutsideSymmetric && m_metrics.isInsideRounded )
573 for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
575 it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
576 it.setBorderLine( Qt::TopRightCorner, linesTR-- );
577 it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
578 it.setBorderLine( Qt::BottomRightCorner, linesBR++ );
583 for ( it.resetSteps( Qt::TopLeftCorner ); !it.isDone(); ++it )
584 it.setBorderLine( Qt::TopLeftCorner, linesTL++ );
586 for ( it.resetSteps( Qt::TopRightCorner ); !it.isDone(); ++it )
587 it.setBorderLine( Qt::TopRightCorner, linesTR-- );
589 for ( it.resetSteps( Qt::BottomLeftCorner ); !it.isDone(); ++it )
590 it.setBorderLine( Qt::BottomLeftCorner, linesBL-- );
592 for ( it.resetSteps( Qt::BottomRightCorner ); !it.isDone(); ++it )
593 it.setBorderLine( Qt::BottomRightCorner, linesBR++ );
596 setBorderGradientLines( lines );
597 lines[ gl.closingOffsets[ 1 ] ] = lines[ gl.closingOffsets[ 0 ] ];
602 Q_ASSERT( !m_isColored );
605 if ( m_metrics.isInsideRounded )
607 const LineMap map( m_metrics );
608 qskCreateFill( m_metrics, map, lines );
612 const auto& in = m_metrics.innerRect;
614 lines[0].setLine( in.left(), in.top(), in.right(), in.top() );
615 lines[1].setLine( in.left(), in.bottom(), in.right(), in.bottom() );
621 Q_ASSERT( m_isColored );
623 Q_ASSERT( !m_colorMap.isTransparent() );
625 if ( m_metrics.isInsideRounded )
627 const FillMap map( m_metrics, m_colorMap );
628 qskCreateFill( m_metrics, map, lines );
632 const auto& in = m_metrics.innerRect;
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 );
642 Q_ASSERT( m_isColored );
643 Q_ASSERT( borderLines || fillLines );
644 Q_ASSERT( fillLines ==
nullptr || !m_colorMap.isTransparent() );
646 if ( m_metrics.isOutsideSymmetric && m_metrics.isInsideRounded )
648 if ( borderLines && fillLines )
655 setBorderAndFillLines( borderLines, fillLines );
661 setBorderLines( borderLines );
664 setFillLines( fillLines );
667void QskBoxBasicStroker::setBorderAndFillLines(
672 const auto& gl = m_geometryLayout;
674 const FillMap fillMap( m_metrics, m_colorMap );
675 CornerIteratorColor it( m_metrics, m_borderColors );
682 const auto stepCount = m_metrics.corners[0].stepCount;
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 ];
689 if ( m_metrics.preferredOrientation == Qt::Horizontal )
691 auto l1 = fillLines + stepCount;
692 auto l2 = fillLines + stepCount + 1;
694 for ( it.resetSteps( TopLeftCorner ); !it.isDone(); ++it )
696 it.setBorderLine( TopLeftCorner, linesTL++ );
697 it.setBorderLine( TopRightCorner, linesTR-- );
698 it.setBorderLine( BottomLeftCorner, linesBL-- );
699 it.setBorderLine( BottomRightCorner, linesBR++ );
701 fillMap.setVLine( TopLeftCorner, BottomLeftCorner, it.cos(), it.sin(), l1-- );
702 fillMap.setVLine( TopRightCorner, BottomRightCorner, it.cos(), it.sin(), l2++ );
708 auto l2 = fillLines + 2 * stepCount + 1;
710 for ( it.resetSteps( TopLeftCorner ); !it.isDone(); ++it )
712 it.setBorderLine( TopLeftCorner, linesTL++ );
713 it.setBorderLine( TopRightCorner, linesTR-- );
714 it.setBorderLine( BottomLeftCorner, linesBL-- );
715 it.setBorderLine( BottomRightCorner, linesBR++ );
717 fillMap.setHLine( TopLeftCorner, TopRightCorner, it.cos(), it.sin(), l1++ );
718 fillMap.setHLine( BottomLeftCorner, BottomRightCorner, it.cos(), it.sin(), l2-- );
724 setBorderGradientLines( borderLines );
725 borderLines[ gl.closingOffsets[ 1 ] ] = borderLines[ gl.closingOffsets[ 0 ] ];
729int QskBoxBasicStroker::borderCount()
const
731 if ( !m_metrics.hasBorder )
734 if ( m_isColored && !m_borderColors.isVisible() )
739 if ( m_metrics.isOutsideRounded )
745 n = m_metrics.outerStepCount() + 4 + 1;
747 if ( m_isColored && !m_borderColors.isMonochrome() )
749 n += gradientLineCount( m_borderColors.left() );
750 n += gradientLineCount( m_borderColors.top() );
751 n += gradientLineCount( m_borderColors.right() );
752 n += gradientLineCount( m_borderColors.bottom() );
763 if ( m_isColored && !m_borderColors.isMonochrome() )
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();
771 n += qMax( gradientLines, 0 );
778int QskBoxBasicStroker::fillCount()
const
780 if ( m_metrics.innerRect.isEmpty() )
783 if ( m_isColored && m_colorMap.isTransparent() )
788 if ( m_metrics.isInsideRounded )
790 if ( m_metrics.preferredOrientation == Qt::Horizontal )
792 n += m_metrics.innerStepCount( Qt::TopLeftCorner, Qt::BottomLeftCorner );
793 n += m_metrics.innerStepCount( Qt::TopRightCorner, Qt::BottomRightCorner );
797 n += m_metrics.innerStepCount( Qt::TopLeftCorner, Qt::TopRightCorner );
798 n += m_metrics.innerStepCount( Qt::BottomLeftCorner, Qt::BottomRightCorner );