6#include "QskBoxGradientStroker.h"
7#include "QskBoxBasicStroker.h"
8#include "QskVertexHelper.h"
9#include "QskBoxMetrics.h"
11static inline bool qskCanUseHVFiller(
14 if ( !dir.isTilted() )
15 return orientations & ( dir.isVertical() ? Qt::Vertical : Qt::Horizontal );
22 using namespace QskVertex;
35 : m_metrics( metrics )
43 const auto corners = m_metrics.corners;
44 const auto dir = gradient.linearDirection();
46 m_isVertical = dir.isVertical();
47 m_t0 = m_isVertical ? dir.y1() : dir.x1();
48 m_dt = m_isVertical ? dir.dy() : dir.dx();
54 m_gradientIterator.reset( gradient.stops() );
56 m_c1 = &corners[ Qt::TopLeftCorner ];
57 m_c2 = &corners[ m_isVertical ? Qt::TopRightCorner : Qt::BottomLeftCorner ];
58 m_c3 = ( m_c1->stepCount > m_c2->stepCount ) ? m_c1 : m_c2;
60 arcIt.reset( m_c3->stepCount, !m_isVertical );
62 v2 = contourValue( arcIt );
64 skipGradientLines( v2.pos );
65 setContourLine( v2, l++ );
72 v2 = contourValue( arcIt );
74 l = setGradientLines( v1, v2, l );
75 setContourLine( v2, l++ );
78 }
while( !arcIt.isDone() );
80 m_c1 = &corners[ m_isVertical ? Qt::BottomLeftCorner : Qt::TopRightCorner ];
81 m_c2 = &corners[ Qt::BottomRightCorner ];
82 m_c3 = ( m_c1->stepCount > m_c2->stepCount ) ? m_c1 : m_c2;
84 arcIt.reset( m_c3->stepCount, m_isVertical );
86 if ( contourValue( arcIt ).pos <= v2.pos )
92 v2 = contourValue( arcIt );
94 l = setGradientLines( v1, v2, l );
95 setContourLine( v2, l++ );
99 }
while( !arcIt.isDone() );
104 inline void skipGradientLines( qreal pos )
106 while ( !m_gradientIterator.isDone() )
108 const auto pos2 = m_t0 + m_gradientIterator.position() * m_dt;
109 if ( pos2 > pos && !qFuzzyIsNull( pos2 - pos ) )
112 m_gradientIterator.advance();
117 const Value& v1,
const Value& v2,
ColoredLine* lines )
119 while ( !m_gradientIterator.isDone() )
121 const auto pos = m_t0 + m_gradientIterator.position() * m_dt;
123 if ( pos > v2.pos || qFuzzyIsNull( v2.pos - pos ) )
126 const auto color = m_gradientIterator.color();
128 const qreal f = ( pos - v1.pos ) / ( v2.pos - v1.pos );
130 const qreal t1 = v1.from + f * ( v2.from - v1.from );
131 const qreal t2 = v1.to + f * ( v2.to - v1.to );
133 setLine( t1, t2, pos, color, lines++ );
135 m_gradientIterator.advance();
141 inline void setContourLine(
const Value& v,
ColoredLine* line )
143 const auto color = m_gradientIterator.colorAt( ( v.pos - m_t0 ) / m_dt );
144 setLine( v.from, v.to, v.pos, color, line );
148 inline Value contourValue(
const ArcIterator& arcIt )
const
150 const auto cos = arcIt.cos();
151 const auto sin = arcIt.sin();
154 return { m_c1->xInner( cos ), m_c2->xInner( cos ), m_c3->yInner( sin ) };
156 return { m_c1->yInner( sin ), m_c2->yInner( sin ), m_c3->xInner( cos ) };
159 inline void setLine( qreal from, qreal to, qreal pos,
163 line->setLine( from, pos, to, pos, color );
165 line->setLine( pos, from, pos, to, color );
174 QskVertex::GradientIterator m_gradientIterator;
180 using namespace QskVertex;
186 inline Point( qreal x, qreal y, qreal v ): x( x ), y( y ), v( v ) {}
198 class TangentLineIterator
203 : m_metrics( metrics )
205 , m_isClockwise( isClockwise )
209 if ( dir.dx() >= 0.0 )
211 if ( dir.dy() >= 0.0 )
213 m_corners[0] = TopLeftCorner;
214 m_corners[1] = m_isClockwise ? TopRightCorner : BottomLeftCorner;
215 m_corners[2] = BottomRightCorner;
219 m_corners[0] = BottomLeftCorner;
220 m_corners[1] = m_isClockwise ? BottomRightCorner : TopLeftCorner;
221 m_corners[2] = TopRightCorner;
226 if ( dir.dy() >= 0.0 )
228 m_corners[0] = TopRightCorner;
229 m_corners[1] = m_isClockwise ? TopLeftCorner : BottomRightCorner;
230 m_corners[2] = BottomLeftCorner;
234 m_corners[0] = BottomRightCorner;
235 m_corners[1] = m_isClockwise ? BottomLeftCorner : TopRightCorner;
236 m_corners[2] = TopLeftCorner;
241 inline QPointF pointAt( qreal value )
const
243 const auto dv = m_p2.v - m_p1.v;
245 return QPointF( m_p1.x, m_p1.y );
247 const qreal r = ( value - m_p1.v ) / dv;
248 return QPointF( m_p1.x + r * ( m_p2.x - m_p1.x ),
249 m_p1.y + r * ( m_p2.y - m_p1.y ) );
254 m_arcIterator = arcIterator;
256 const auto& c = m_metrics.corners[ m_corners[0] ];
258 const qreal x = c.xInner( m_arcIterator.cos() );
259 const qreal y = c.yInner( m_arcIterator.sin() );
261 m_p2 = m_p1 = { x, y, m_dir.valueAt( x, y ) };
264 inline bool isDone()
const {
return m_isDone; }
266 inline qreal value()
const {
return m_p2.v; }
267 inline Qt::Corner corner()
const {
return m_corners[ m_cornerIndex ]; }
269 inline Point p1()
const {
return m_p1; }
270 inline Point p2()
const {
return m_p2; }
272 void incrementTo( qreal value )
274 while ( !isDone() && ( value > m_p2.v ) )
278 inline void advance()
283 m_arcIterator.increment();
285 auto c = &m_metrics.corners[ m_corners[ m_cornerIndex ] ];
287 if( m_arcIterator.isDone() )
289 if ( m_cornerIndex < 2 )
291 c = &m_metrics.corners[ m_corners[ ++m_cornerIndex ] ];
292 m_arcIterator.reset( c->innerStepCount(), !m_arcIterator.isInverted() );
298 m_p2.x = c->xInner( m_arcIterator.cos() );
299 m_p2.y = c->yInner( m_arcIterator.sin() );
301 m_p2.v = m_dir.valueAt( m_p2.x, m_p2.y );
303 if ( m_cornerIndex == 2 )
305 if ( ( m_p2.v < m_p1.v ) || m_arcIterator.isDone() )
318 const bool m_isClockwise;
319 bool m_isDone =
false;
325 int m_cornerIndex = 0;
326 Qt::Corner m_corners[3];
338 class OutlineIterator
343 : m_isVertical( dir.dx() == 0.0 )
344 , m_it1( metrics, dir, clockwise )
345 , m_it2( metrics, dir, !clockwise )
347 const auto corner = m_it1.corner();
348 const auto& c = metrics.corners[ m_it1.corner() ];
350 bool inverted = clockwise;
351 if ( corner == Qt::TopLeftCorner || corner == Qt::BottomRightCorner )
352 inverted = clockwise;
354 inverted = !clockwise;
357 arcIt.reset( c.innerStepCount(), inverted );
359 if ( c.innerStepCount() == 1 )
361 m_it1.setup( arcIt );
362 m_it2.setup( arcIt.reverted() );
370 arcIt.reset( c.innerStepCount(), clockwise );
372 qreal v1 = dir.valueAt( c.xInner( arcIt.cos() ), c.yInner( arcIt.sin() ) );
378 const qreal v2 = dir.valueAt(
379 c.xInner( arcIt.cos() ), c.yInner( arcIt.sin() ) );
386 }
while( !arcIt.isDone() );
390 m_it1.setup( arcIt );
391 m_it2.setup( arcIt.reverted() );
394 m_it2.incrementTo( m_it1.value() );
397 inline void advance()
408 if ( !m_it1.isDone() )
409 m_it2.incrementTo( m_it1.value() );
412 inline bool isDone()
const {
return m_it1.isDone(); }
413 inline qreal value()
const {
return m_it1.value(); }
417 const auto p1 = m_it1.pointAt( value );
418 const auto p2 = m_it2.pointAt( value );
420 setLine( p1.x(), p1.y(), p2.x(), p2.y(), color, line );
425 const auto p1 = m_it1.p2();
426 const auto p2 = m_it2.pointAt( p1.v );
428 setLine( p1.x, p1.y, p2.x(), p2.y(), color, line );
432 inline void setLine( qreal x1, qreal y1, qreal x2, qreal y2,
438 line->setLine( x1, y1, x2, y2, color );
440 line->setLine( x2, y2, x1, y1, color );
445 line->setLine( x1, y1, x2, y2, color );
447 line->setLine( x2, y2, x1, y1, color );
457 const bool m_isVertical;
458 TangentLineIterator m_it1, m_it2;
470 class ContourIterator
475 : m_left( metrics, dir, false )
476 , m_right( metrics, dir, true )
484 m_next->setLineAt( value, color, line );
489 m_next->setLine( color, line );
492 inline qreal value()
const {
return m_next->value(); }
494 inline bool advance()
496 if ( qFuzzyIsNull( m_left.value() - m_right.value() ) )
506 if ( m_next->isDone() )
509 m_next = ( m_left.value() < m_right.value() ) ? &m_left : &m_right;
515 OutlineIterator m_left, m_right;
516 OutlineIterator* m_next;
523 : m_metrics( metrics )
529 ContourIterator it( m_metrics, gradient.linearDirection() );
530 QskVertex::GradientIterator gradientIt( gradient.stops() );
535 while ( !gradientIt.isDone() )
537 const auto pos = gradientIt.position();
538 if ( pos > it.value() || qFuzzyIsNull( pos - it.value() ) )
541 gradientIt.advance();
547 while ( !gradientIt.isDone() )
549 const auto pos = gradientIt.position();
550 if ( pos > it.value() || qFuzzyIsNull( pos - it.value() ) )
553 it.setGradientLine( pos, gradientIt.color(), l++ );
555 gradientIt.advance();
559 const auto color = gradientIt.colorAt( it.value() );
560 it.setContourLine( color, l++ );
562 }
while ( it.advance() );
575 : m_metrics( metrics )
581 const qreal x1 = m_metrics.innerRect.left();
582 const qreal x2 = m_metrics.innerRect.right();
583 const qreal y1 = m_metrics.innerRect.top();
584 const qreal y2 = m_metrics.innerRect.bottom();
586 QskVertex::GradientIterator it( gradient.stops() );
589 const auto dir = gradient.linearDirection();
591 if ( dir.isTilted() )
593 const qreal m = dir.dy() / dir.dx();
594 const auto vec = dir.vector();
596 struct { qreal x, y, value; } c1, c2, c3, c4;
600 c1 = { x1, y1, dir.valueAt( x1, y1 ) };
601 c2 = { x2, y1, dir.valueAt( x2, y1 ) };
602 c3 = { x1, y2, dir.valueAt( x1, y2 ) };
603 c4 = { x2, y2, dir.valueAt( x2, y2 ) };
611 if ( c1.value > c4.value )
614 if ( c2.value > c3.value )
619 while ( !it.isDone() && ( it.position() <= c1.value ) )
622 setLine( c1.x, c1.y, c1.x, c1.y, it.colorAt( c1.value ), l++ );
624 while ( !it.isDone() && ( it.position() < c2.value ) )
626 const auto p = vec.pointAt( it.position() );
628 const qreal ly1 = p.y() + ( p.x() - c1.x ) / m;
629 const qreal lx2 = p.x() + ( p.y() - c1.y ) * m;
631 setLine( c1.x, ly1, lx2, c1.y, it.color(), l++ );
637 const auto dy = ( c2.x - c3.x ) / m;
639 setLine( c2.x, c2.y, c3.x, c2.y + dy, it.colorAt( c2.value ), l++ );
641 while ( !it.isDone() && ( it.position() < c3.value ) )
643 const auto p = vec.pointAt( it.position() );
645 const qreal ly1 = p.y() + ( p.x() - c2.x ) / m;
646 const qreal ly2 = p.y() + ( p.x() - c3.x ) / m;
648 setLine( c2.x, ly1, c3.x, ly2, it.color(), l++ );
652 setLine( c2.x, c3.y - dy, c3.x, c3.y, it.colorAt( c3.value ), l++ );
656 const qreal dx = ( c2.y - c3.y ) * m;
658 setLine( c2.x, c2.y, c2.x + dx, c3.y, it.colorAt( c2.value ), l++ );
660 while ( !it.isDone() && ( it.position() < c3.value ) )
662 const auto p = vec.pointAt( it.position() );
664 const qreal lx1 = p.x() + ( p.y() - c2.y ) * m;
665 const qreal lx2 = p.x() + ( p.y() - c3.y ) * m;
667 setLine( lx1, c2.y, lx2, c3.y, it.color(), l++ );
671 setLine( c3.x - dx, c2.y, c3.x, c3.y, it.colorAt( c3.value ), l++ );
674 while ( !it.isDone() && ( it.position() < c4.value ) )
676 const auto p = vec.pointAt( it.position() );
678 const qreal ly1 = p.y() + ( p.x() - c4.x ) / m;
679 const qreal lx2 = p.x() + ( p.y() - c4.y ) * m;
681 setLine( c4.x, ly1, lx2, c4.y, it.color(), l++ );
685 setLine( c4.x, c4.y, c4.x, c4.y, it.colorAt( c4.value ), l++ );
687 else if ( dir.isVertical() )
689 Q_ASSERT( dir.dy() > 0.0 );
691 const qreal min = ( y1 - dir.y1() ) / dir.dy();
692 const qreal max = ( y2 - dir.y1() ) / dir.dy();
694 while ( !it.isDone() && ( it.position() <= min ) )
697 setHLine( y1, it.colorAt( min ), l++ );
699 while ( !it.isDone() && ( it.position() < max ) )
701 const auto y = dir.y1() + it.position() * dir.dy();
702 setHLine( y, it.color(), l++ );
707 setHLine( y2, it.colorAt( max ), l++ );
711 Q_ASSERT( dir.dx() > 0.0 );
713 const qreal min = ( x1 - dir.x1() ) / dir.dx();
714 const qreal max = ( x2 - dir.x1() ) / dir.dx();
716 while ( !it.isDone() && ( it.position() <= min ) )
719 setVLine( x1, it.colorAt( min ), l++ );
721 while ( !it.isDone() && ( it.position() < max ) )
723 const auto x = dir.x1() + it.position() * dir.dx();
724 setVLine( x, it.color(), l++ );
729 setVLine( x2, it.colorAt( max ), l++ );
737 inline void setHLine( qreal y,
740 const auto& r = m_metrics.innerRect;
741 line->setLine( r.left(), y, r.right(), y, color );
744 inline void setVLine( qreal x,
747 const auto& r = m_metrics.innerRect;
748 line->setLine( x, r.top(), x, r.bottom(), color );
751 inline void setLine( qreal x1, qreal y1, qreal x2, qreal y2,
755 line->setLine( x1, y1, x2, y2, color );
757 line->setLine( x2, y2, x1, y1, color );
764QskBoxGradientStroker::QskBoxGradientStroker(
766 : m_metrics( metrics )
767 , m_gradient( gradient )
768 , m_dir( m_gradient.linearDirection() )
773int QskBoxGradientStroker::lineCount()
const
775 if ( m_metrics.innerRect.isEmpty() || !m_gradient.isVisible() )
778 int n = m_gradient.stepCount() - 1;
780 if ( m_metrics.isInsideRounded )
782 if ( qskCanUseHVFiller( m_metrics.stepSymmetries, m_dir ) )
785 n += stroker.fillCount();
789 n += m_metrics.innerStepCount() + 4;
796 if ( m_dir.isTilted() )
804 if ( !m_dir.contains( m_metrics.innerRect ) )
814 if ( m_metrics.isInsideRounded )
816 if ( qskCanUseHVFiller( m_metrics.stepSymmetries, m_dir ) )
818 FillerHV filler( m_metrics );
819 effectiveCount = filler.setLines( m_gradient, lines );
823 FillerD filler( m_metrics );
824 effectiveCount = filler.setLines( m_gradient, lines );
829 FillerRect filler( m_metrics );
830 effectiveCount = filler.setLines( m_gradient, lines );
834 qDebug() <<
"expected:" << lineCount <<
", got:" << effectiveCount;
839 if ( effectiveCount > lineCount )
841 qFatal(
"geometry: allocated memory exceeded: %d vs. %d",
842 effectiveCount, lineCount );
845 if ( effectiveCount < lineCount )
855 for (
int i = effectiveCount; i < lineCount; i++ )
856 lines[i] = lines[i-1];