QSkinny 0.8.0
C++/Qt UI toolkit
Loading...
Searching...
No Matches
QskBoxGradientStroker.cpp
1/******************************************************************************
2 * QSkinny - Copyright (C) The authors
3 * SPDX-License-Identifier: BSD-3-Clause
4 *****************************************************************************/
5
6#include "QskBoxGradientStroker.h"
7#include "QskBoxBasicStroker.h"
8#include "QskVertexHelper.h"
9#include "QskBoxMetrics.h"
10
11static inline bool qskCanUseHVFiller(
12 const Qt::Orientations orientations, const QskLinearDirection& dir )
13{
14 if ( !dir.isTilted() )
15 return orientations & ( dir.isVertical() ? Qt::Vertical : Qt::Horizontal );
16
17 return false;
18}
19
20namespace
21{
22 using namespace QskVertex;
23
24 class Value
25 {
26 public:
27 qreal from, to; // opposite to the direction of the gradient
28 qreal pos; // in direction of the gradient
29 };
30
31 class FillerHV
32 {
33 public:
34 FillerHV( const QskBoxMetrics& metrics )
35 : m_metrics( metrics )
36 {
37 }
38
39 int setLines( const QskGradient& gradient, ColoredLine* lines )
40 {
41 using namespace Qt;
42
43 const auto corners = m_metrics.corners;
44 const auto dir = gradient.linearDirection();
45
46 m_isVertical = dir.isVertical();
47 m_t0 = m_isVertical ? dir.y1() : dir.x1();
48 m_dt = m_isVertical ? dir.dy() : dir.dx();
49
50 ColoredLine* l = lines;
51 Value v1, v2;
52 ArcIterator arcIt;
53
54 m_gradientIterator.reset( gradient.stops() );
55
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;
59
60 arcIt.reset( m_c3->stepCount, !m_isVertical );
61
62 v2 = contourValue( arcIt );
63
64 skipGradientLines( v2.pos );
65 setContourLine( v2, l++ );
66
67 arcIt.increment();
68
69 do
70 {
71 v1 = v2;
72 v2 = contourValue( arcIt );
73
74 l = setGradientLines( v1, v2, l );
75 setContourLine( v2, l++ );
76
77 arcIt.increment();
78 } while( !arcIt.isDone() );
79
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;
83
84 arcIt.reset( m_c3->stepCount, m_isVertical );
85
86 if ( contourValue( arcIt ).pos <= v2.pos )
87 arcIt.increment(); // ellipse: opening/closing parts are connected
88
89 do
90 {
91 v1 = v2;
92 v2 = contourValue( arcIt );
93
94 l = setGradientLines( v1, v2, l );
95 setContourLine( v2, l++ );
96
97 arcIt.increment();
98
99 } while( !arcIt.isDone() );
100
101 return l - lines;
102 }
103
104 inline void skipGradientLines( qreal pos )
105 {
106 while ( !m_gradientIterator.isDone() )
107 {
108 const auto pos2 = m_t0 + m_gradientIterator.position() * m_dt;
109 if ( pos2 > pos && !qFuzzyIsNull( pos2 - pos ) )
110 return;
111
112 m_gradientIterator.advance();
113 }
114 }
115
116 inline ColoredLine* setGradientLines(
117 const Value& v1, const Value& v2, ColoredLine* lines )
118 {
119 while ( !m_gradientIterator.isDone() )
120 {
121 const auto pos = m_t0 + m_gradientIterator.position() * m_dt;
122
123 if ( pos > v2.pos || qFuzzyIsNull( v2.pos - pos ) )
124 return lines;
125
126 const auto color = m_gradientIterator.color();
127
128 const qreal f = ( pos - v1.pos ) / ( v2.pos - v1.pos );
129
130 const qreal t1 = v1.from + f * ( v2.from - v1.from );
131 const qreal t2 = v1.to + f * ( v2.to - v1.to );
132
133 setLine( t1, t2, pos, color, lines++ );
134
135 m_gradientIterator.advance();
136 }
137
138 return lines;
139 }
140
141 inline void setContourLine( const Value& v, ColoredLine* line )
142 {
143 const auto color = m_gradientIterator.colorAt( ( v.pos - m_t0 ) / m_dt );
144 setLine( v.from, v.to, v.pos, color, line );
145 }
146
147 private:
148 inline Value contourValue( const ArcIterator& arcIt ) const
149 {
150 const auto cos = arcIt.cos();
151 const auto sin = arcIt.sin();
152
153 if ( m_isVertical )
154 return { m_c1->xInner( cos ), m_c2->xInner( cos ), m_c3->yInner( sin ) };
155 else
156 return { m_c1->yInner( sin ), m_c2->yInner( sin ), m_c3->xInner( cos ) };
157 }
158
159 inline void setLine( qreal from, qreal to, qreal pos,
160 Color color, ColoredLine* line )
161 {
162 if ( m_isVertical )
163 line->setLine( from, pos, to, pos, color );
164 else
165 line->setLine( pos, from, pos, to, color );
166 }
167
168 const QskBoxMetrics& m_metrics;
169
170 bool m_isVertical;
171 qreal m_t0, m_dt;
172
173 const QskBoxMetrics::Corner* m_c1, * m_c2, * m_c3;
174 QskVertex::GradientIterator m_gradientIterator;
175 };
176}
177
178namespace
179{
180 using namespace QskVertex;
181
182 class Point
183 {
184 public:
185 Point() = default;
186 inline Point( qreal x, qreal y, qreal v ): x( x ), y( y ), v( v ) {}
187
188 qreal x = 0;
189 qreal y = 0;
190 qreal v = 0; // value at (x,y) according the gradient vector in the range [0,1]
191 };
192
193 /*
194 Iterating over the interpolating ( ~tangent ) lines of the outline in order
195 of increasing gradient values. We have the options to run clockwise or
196 counter-clockwise
197 */
198 class TangentLineIterator
199 {
200 public:
201 TangentLineIterator( const QskBoxMetrics& metrics,
202 const QskLinearDirection& dir, bool isClockwise )
203 : m_metrics( metrics )
204 , m_dir( dir )
205 , m_isClockwise( isClockwise )
206 {
207 using namespace Qt;
208
209 if ( dir.dx() >= 0.0 )
210 {
211 if ( dir.dy() >= 0.0 )
212 {
213 m_corners[0] = TopLeftCorner;
214 m_corners[1] = m_isClockwise ? TopRightCorner : BottomLeftCorner;
215 m_corners[2] = BottomRightCorner;
216 }
217 else
218 {
219 m_corners[0] = BottomLeftCorner;
220 m_corners[1] = m_isClockwise ? BottomRightCorner : TopLeftCorner;
221 m_corners[2] = TopRightCorner;
222 }
223 }
224 else
225 {
226 if ( dir.dy() >= 0.0 )
227 {
228 m_corners[0] = TopRightCorner;
229 m_corners[1] = m_isClockwise ? TopLeftCorner : BottomRightCorner;
230 m_corners[2] = BottomLeftCorner;
231 }
232 else
233 {
234 m_corners[0] = BottomRightCorner;
235 m_corners[1] = m_isClockwise ? BottomLeftCorner : TopRightCorner;
236 m_corners[2] = TopLeftCorner;
237 }
238 }
239 }
240
241 inline QPointF pointAt( qreal value ) const
242 {
243 const auto dv = m_p2.v - m_p1.v;
244 if ( dv == 0.0 )
245 return QPointF( m_p1.x, m_p1.y );
246
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 ) );
250 }
251
252 void setup( const QskVertex::ArcIterator& arcIterator )
253 {
254 m_arcIterator = arcIterator;
255
256 const auto& c = m_metrics.corners[ m_corners[0] ];
257
258 const qreal x = c.xInner( m_arcIterator.cos() );
259 const qreal y = c.yInner( m_arcIterator.sin() );
260
261 m_p2 = m_p1 = { x, y, m_dir.valueAt( x, y ) };
262 }
263
264 inline bool isDone() const { return m_isDone; }
265
266 inline qreal value() const { return m_p2.v; }
267 inline Qt::Corner corner() const { return m_corners[ m_cornerIndex ]; }
268
269 inline Point p1() const { return m_p1; }
270 inline Point p2() const { return m_p2; }
271
272 void incrementTo( qreal value )
273 {
274 while ( !isDone() && ( value > m_p2.v ) )
275 advance();
276 }
277
278 inline void advance()
279 {
280 if ( m_isDone )
281 return;
282
283 m_arcIterator.increment();
284
285 auto c = &m_metrics.corners[ m_corners[ m_cornerIndex ] ];
286
287 if( m_arcIterator.isDone() )
288 {
289 if ( m_cornerIndex < 2 )
290 {
291 c = &m_metrics.corners[ m_corners[ ++m_cornerIndex ] ];
292 m_arcIterator.reset( c->innerStepCount(), !m_arcIterator.isInverted() );
293 }
294 }
295
296 m_p1 = m_p2;
297
298 m_p2.x = c->xInner( m_arcIterator.cos() );
299 m_p2.y = c->yInner( m_arcIterator.sin() );
300
301 m_p2.v = m_dir.valueAt( m_p2.x, m_p2.y );
302
303 if ( m_cornerIndex == 2 )
304 {
305 if ( ( m_p2.v < m_p1.v ) || m_arcIterator.isDone() )
306 {
307 // passing the opposite position from our starting point
308 m_p2 = m_p1;
309 m_isDone = true;
310 }
311 }
312 }
313
314 private:
315 const QskBoxMetrics& m_metrics;
316 const QskLinearDirection m_dir;
317
318 const bool m_isClockwise;
319 bool m_isDone = false;
320
321 QskVertex::ArcIterator m_arcIterator;
322
323 Point m_p1, m_p2;
324
325 int m_cornerIndex = 0;
326 Qt::Corner m_corners[3];
327 };
328
329 /*
330 OutlineIterator iterates a pair of tangent lines in direction of the gradient vector.
331 The second tangent line increases in oppsite direction so that
332 the value of the leading point of the first tangent line is in between the values
333 of the opposite tangent line.
334
335 Contour and gradient lines can then be found from cutting the tangent lines
336 with the perpendicular of the gradient vector.
337 */
338 class OutlineIterator
339 {
340 public:
341 OutlineIterator( const QskBoxMetrics& metrics,
342 const QskLinearDirection& dir, bool clockwise )
343 : m_isVertical( dir.dx() == 0.0 )
344 , m_it1( metrics, dir, clockwise )
345 , m_it2( metrics, dir, !clockwise )
346 {
347 const auto corner = m_it1.corner();
348 const auto& c = metrics.corners[ m_it1.corner() ];
349
350 bool inverted = clockwise;
351 if ( corner == Qt::TopLeftCorner || corner == Qt::BottomRightCorner )
352 inverted = clockwise;
353 else
354 inverted = !clockwise;
355
357 arcIt.reset( c.innerStepCount(), inverted );
358
359 if ( c.innerStepCount() == 1 )
360 {
361 m_it1.setup( arcIt );
362 m_it2.setup( arcIt.reverted() );
363
364 // not rounded
365 m_it1.advance();
366 m_it2.advance();
367 }
368 else
369 {
370 arcIt.reset( c.innerStepCount(), clockwise );
371
372 qreal v1 = dir.valueAt( c.xInner( arcIt.cos() ), c.yInner( arcIt.sin() ) );
373
374 do
375 {
376 arcIt.increment();
377
378 const qreal v2 = dir.valueAt(
379 c.xInner( arcIt.cos() ), c.yInner( arcIt.sin() ) );
380
381 if ( v2 > v1 )
382 break;
383
384 v1 = v2;
385
386 } while( !arcIt.isDone() );
387
388 arcIt.decrement();
389
390 m_it1.setup( arcIt );
391 m_it2.setup( arcIt.reverted() );
392 }
393
394 m_it2.incrementTo( m_it1.value() );
395 }
396
397 inline void advance()
398 {
399 m_it1.advance();
400
401 /*
402 Increasing the opposite iterator until its value becomes larger
403 than the value of the leading iterator. Then the opposite
404 point for the next line can be found by interpolating
405 between p1/p2 of the opposite iterator.
406 */
407
408 if ( !m_it1.isDone() )
409 m_it2.incrementTo( m_it1.value() );
410 }
411
412 inline bool isDone() const { return m_it1.isDone(); }
413 inline qreal value() const { return m_it1.value(); }
414
415 inline void setLineAt( qreal value, Color color, ColoredLine* line )
416 {
417 const auto p1 = m_it1.pointAt( value );
418 const auto p2 = m_it2.pointAt( value );
419
420 setLine( p1.x(), p1.y(), p2.x(), p2.y(), color, line );
421 }
422
423 inline void setLine( Color color, ColoredLine* line )
424 {
425 const auto p1 = m_it1.p2();
426 const auto p2 = m_it2.pointAt( p1.v );
427
428 setLine( p1.x, p1.y, p2.x(), p2.y(), color, line );
429 }
430
431 private:
432 inline void setLine( qreal x1, qreal y1, qreal x2, qreal y2,
433 Color color, ColoredLine* line )
434 {
435 if ( m_isVertical )
436 {
437 if ( x1 < x2 )
438 line->setLine( x1, y1, x2, y2, color );
439 else
440 line->setLine( x2, y2, x1, y1, color );
441 }
442 else
443 {
444 if ( y1 < y2 )
445 line->setLine( x1, y1, x2, y2, color );
446 else
447 line->setLine( x2, y2, x1, y1, color );
448 }
449 }
450
451 /*
452 The first iterator for running along the left or right
453 half of the ellipse. The other one is for finding the
454 corresponing point at the other side.
455 */
456
457 const bool m_isVertical;
458 TangentLineIterator m_it1, m_it2;
459 };
460
461 /*
462 ContourIterator runs a pair of OutlineIterators, one clockwise the other
463 counter clockwise in direction of the gradient vector, so that we
464 always pass all corners.
465
466 The next contour line can always be found from the outline iterator with the
467 larger gradient value. Gradient lines can then be found from interpolating
468 between the tangent lines of the outline iterators.
469 */
470 class ContourIterator
471 {
472 public:
473 ContourIterator(
474 const QskBoxMetrics& metrics, const QskLinearDirection& dir )
475 : m_left( metrics, dir, false )
476 , m_right( metrics, dir, true )
477 {
478 m_right.advance();
479 m_next = &m_left;
480 }
481
482 inline void setGradientLine( qreal value, Color color, ColoredLine* line )
483 {
484 m_next->setLineAt( value, color, line );
485 }
486
487 inline void setContourLine( Color color, ColoredLine* line )
488 {
489 m_next->setLine( color, line );
490 }
491
492 inline qreal value() const { return m_next->value(); }
493
494 inline bool advance()
495 {
496 if ( qFuzzyIsNull( m_left.value() - m_right.value() ) )
497 {
498 m_left.advance();
499 m_right.advance();
500 }
501 else
502 {
503 m_next->advance();
504 }
505
506 if ( m_next->isDone() )
507 return false;
508
509 m_next = ( m_left.value() < m_right.value() ) ? &m_left : &m_right;
510
511 return true;
512 }
513
514 private:
515 OutlineIterator m_left, m_right;
516 OutlineIterator* m_next;
517 };
518
519 class FillerD
520 {
521 public:
522 FillerD( const QskBoxMetrics& metrics )
523 : m_metrics( metrics )
524 {
525 }
526
527 int setLines( const QskGradient& gradient, ColoredLine* lines )
528 {
529 ContourIterator it( m_metrics, gradient.linearDirection() );
530 QskVertex::GradientIterator gradientIt( gradient.stops() );
531
532 ColoredLine* l = lines;
533
534 // skip leading gradient lines
535 while ( !gradientIt.isDone() )
536 {
537 const auto pos = gradientIt.position();
538 if ( pos > it.value() || qFuzzyIsNull( pos - it.value() ) )
539 break;
540
541 gradientIt.advance();
542 }
543
544 do
545 {
546 // gradient lines
547 while ( !gradientIt.isDone() )
548 {
549 const auto pos = gradientIt.position();
550 if ( pos > it.value() || qFuzzyIsNull( pos - it.value() ) )
551 break;
552
553 it.setGradientLine( pos, gradientIt.color(), l++ );
554
555 gradientIt.advance();
556 }
557
558 // contour line
559 const auto color = gradientIt.colorAt( it.value() );
560 it.setContourLine( color, l++ );
561
562 } while ( it.advance() );
563
564 return l - lines;
565 }
566
567 private:
568 const QskBoxMetrics& m_metrics;
569 };
570
571 class FillerRect
572 {
573 public:
574 FillerRect( const QskBoxMetrics& metrics )
575 : m_metrics( metrics )
576 {
577 }
578
579 int setLines( const QskGradient& gradient, ColoredLine* lines ) const
580 {
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();
585
586 QskVertex::GradientIterator it( gradient.stops() );
587 ColoredLine* l = lines;
588
589 const auto dir = gradient.linearDirection();
590
591 if ( dir.isTilted() )
592 {
593 const qreal m = dir.dy() / dir.dx();
594 const auto vec = dir.vector();
595
596 struct { qreal x, y, value; } c1, c2, c3, c4;
597
598 {
599 // corners sorted in order their values
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 ) };
604
605 if ( m < 0.0 )
606 {
607 qSwap( c1, c3 );
608 qSwap( c2, c4 );
609 }
610
611 if ( c1.value > c4.value )
612 qSwap( c1, c4 );
613
614 if ( c2.value > c3.value )
615 qSwap( c2, c3 );
616 }
617
618 // skipping all gradient lines before the first corner
619 while ( !it.isDone() && ( it.position() <= c1.value ) )
620 it.advance();
621
622 setLine( c1.x, c1.y, c1.x, c1.y, it.colorAt( c1.value ), l++ );
623
624 while ( !it.isDone() && ( it.position() < c2.value ) )
625 {
626 const auto p = vec.pointAt( it.position() );
627
628 const qreal ly1 = p.y() + ( p.x() - c1.x ) / m;
629 const qreal lx2 = p.x() + ( p.y() - c1.y ) * m;
630
631 setLine( c1.x, ly1, lx2, c1.y, it.color(), l++ );
632 it.advance();
633 }
634
635 if ( c1.x == c3.x ) // cutting left/right edges
636 {
637 const auto dy = ( c2.x - c3.x ) / m;
638
639 setLine( c2.x, c2.y, c3.x, c2.y + dy, it.colorAt( c2.value ), l++ );
640
641 while ( !it.isDone() && ( it.position() < c3.value ) )
642 {
643 const auto p = vec.pointAt( it.position() );
644
645 const qreal ly1 = p.y() + ( p.x() - c2.x ) / m;
646 const qreal ly2 = p.y() + ( p.x() - c3.x ) / m;
647
648 setLine( c2.x, ly1, c3.x, ly2, it.color(), l++ );
649 it.advance();
650 }
651
652 setLine( c2.x, c3.y - dy, c3.x, c3.y, it.colorAt( c3.value ), l++ );
653 }
654 else // cutting top/bottom edges
655 {
656 const qreal dx = ( c2.y - c3.y ) * m;
657
658 setLine( c2.x, c2.y, c2.x + dx, c3.y, it.colorAt( c2.value ), l++ );
659
660 while ( !it.isDone() && ( it.position() < c3.value ) )
661 {
662 const auto p = vec.pointAt( it.position() );
663
664 const qreal lx1 = p.x() + ( p.y() - c2.y ) * m;
665 const qreal lx2 = p.x() + ( p.y() - c3.y ) * m;
666
667 setLine( lx1, c2.y, lx2, c3.y, it.color(), l++ );
668 it.advance();
669 }
670
671 setLine( c3.x - dx, c2.y, c3.x, c3.y, it.colorAt( c3.value ), l++ );
672 }
673
674 while ( !it.isDone() && ( it.position() < c4.value ) )
675 {
676 const auto p = vec.pointAt( it.position() );
677
678 const qreal ly1 = p.y() + ( p.x() - c4.x ) / m;
679 const qreal lx2 = p.x() + ( p.y() - c4.y ) * m;
680
681 setLine( c4.x, ly1, lx2, c4.y, it.color(), l++ );
682 it.advance();
683 }
684
685 setLine( c4.x, c4.y, c4.x, c4.y, it.colorAt( c4.value ), l++ );
686 }
687 else if ( dir.isVertical() )
688 {
689 Q_ASSERT( dir.dy() > 0.0 ); // normalized in QskBoxRenderer
690
691 const qreal min = ( y1 - dir.y1() ) / dir.dy();
692 const qreal max = ( y2 - dir.y1() ) / dir.dy();
693
694 while ( !it.isDone() && ( it.position() <= min ) )
695 it.advance();
696
697 setHLine( y1, it.colorAt( min ), l++ );
698
699 while ( !it.isDone() && ( it.position() < max ) )
700 {
701 const auto y = dir.y1() + it.position() * dir.dy();
702 setHLine( y, it.color(), l++ );
703
704 it.advance();
705 }
706
707 setHLine( y2, it.colorAt( max ), l++ );
708 }
709 else // dir.isHorizontal
710 {
711 Q_ASSERT( dir.dx() > 0.0 ); // normalized in QskBoxRenderer
712
713 const qreal min = ( x1 - dir.x1() ) / dir.dx();
714 const qreal max = ( x2 - dir.x1() ) / dir.dx();
715
716 while ( !it.isDone() && ( it.position() <= min ) )
717 it.advance();
718
719 setVLine( x1, it.colorAt( min ), l++ );
720
721 while ( !it.isDone() && ( it.position() < max ) )
722 {
723 const auto x = dir.x1() + it.position() * dir.dx();
724 setVLine( x, it.color(), l++ );
725
726 it.advance();
727 }
728
729 setVLine( x2, it.colorAt( max ), l++ );
730 }
731
732 return l - lines;
733 }
734
735 private:
736
737 inline void setHLine( qreal y,
738 QskVertex::Color color, QskVertex::ColoredLine* line ) const
739 {
740 const auto& r = m_metrics.innerRect;
741 line->setLine( r.left(), y, r.right(), y, color );
742 }
743
744 inline void setVLine( qreal x,
745 QskVertex::Color color, QskVertex::ColoredLine* line ) const
746 {
747 const auto& r = m_metrics.innerRect;
748 line->setLine( x, r.top(), x, r.bottom(), color );
749 }
750
751 inline void setLine( qreal x1, qreal y1, qreal x2, qreal y2,
752 Color color, ColoredLine* line ) const
753 {
754 if ( x1 <= x2 )
755 line->setLine( x1, y1, x2, y2, color );
756 else
757 line->setLine( x2, y2, x1, y1, color );
758 }
759
760 const QskBoxMetrics& m_metrics;
761 };
762}
763
764QskBoxGradientStroker::QskBoxGradientStroker(
765 const QskBoxMetrics& metrics, const QskGradient& gradient )
766 : m_metrics( metrics )
767 , m_gradient( gradient )
768 , m_dir( m_gradient.linearDirection() )
769{
770
771}
772
773int QskBoxGradientStroker::lineCount() const
774{
775 if ( m_metrics.innerRect.isEmpty() || !m_gradient.isVisible() )
776 return 0;
777
778 int n = m_gradient.stepCount() - 1;
779
780 if ( m_metrics.isInsideRounded )
781 {
782 if ( qskCanUseHVFiller( m_metrics.stepSymmetries, m_dir ) )
783 {
784 const QskBoxBasicStroker stroker( m_metrics, QskBoxBorderColors(), m_gradient );
785 n += stroker.fillCount();
786 }
787 else
788 {
789 n += m_metrics.innerStepCount() + 4;
790 }
791 }
792 else
793 {
794 n += 2;
795
796 if ( m_dir.isTilted() )
797 n += 2; // contour lines for the opposite corners
798 }
799
800 /*
801 The gradient starts and/or ends inside of the rectangle
802 and we have to add pad with extra gradient lines.
803 */
804 if ( !m_dir.contains( m_metrics.innerRect ) )
805 n += 2;
806
807 return n;
808}
809
810void QskBoxGradientStroker::setLines( int lineCount, QskVertex::ColoredLine* lines )
811{
812 int effectiveCount;
813
814 if ( m_metrics.isInsideRounded )
815 {
816 if ( qskCanUseHVFiller( m_metrics.stepSymmetries, m_dir ) )
817 {
818 FillerHV filler( m_metrics );
819 effectiveCount = filler.setLines( m_gradient, lines );
820 }
821 else
822 {
823 FillerD filler( m_metrics );
824 effectiveCount = filler.setLines( m_gradient, lines );
825 }
826 }
827 else // rectangle
828 {
829 FillerRect filler( m_metrics );
830 effectiveCount = filler.setLines( m_gradient, lines );
831 }
832
833#if 0
834 qDebug() << "expected:" << lineCount << ", got:" << effectiveCount;
835#endif
836
837 if ( lineCount > 0 )
838 {
839 if ( effectiveCount > lineCount )
840 {
841 qFatal( "geometry: allocated memory exceeded: %d vs. %d",
842 effectiveCount, lineCount );
843 }
844
845 if ( effectiveCount < lineCount )
846 {
847 /*
848 Gradient or contour lines might be at the same position
849 and we end up with less lines, than expected.
850 As it is hard to precalculate all corner cases we allow a defensive
851 allocaton policy and simply fill up the line buffer with duplicates
852 of the last line.
853 */
854
855 for ( int i = effectiveCount; i < lineCount; i++ )
856 lines[i] = lines[i-1];
857 }
858 }
859}