1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qrasterizer_p.h"
5
6#include <QPoint>
7#include <QRect>
8
9#include <private/qmath_p.h>
10#include <private/qdatabuffer_p.h>
11#include <private/qdrawhelper_p.h>
12
13#include <QtGui/qpainterpath.h>
14
15#include <algorithm>
16
17QT_BEGIN_NAMESPACE
18
19#if Q_PROCESSOR_WORDSIZE == 8
20typedef qint64 QScFixed;
21#else
22typedef int QScFixed;
23#endif
24#define QScFixedToFloat(i) ((i) * (1./65536.))
25#define FloatToQScFixed(i) (QScFixed)((i) * 65536.)
26#define IntToQScFixed(i) ((QScFixed)(i) * (1 << 16))
27#define QScFixedToInt(i) ((i) >> 16)
28#define QScFixedFactor 65536
29#define FTPosToQScFixed(i) ((QScFixed)(i) * (1 << 10))
30
31#define QScFixedMultiply(x, y) (QScFixed)((qlonglong(x) * qlonglong(y)) >> 16)
32#define QScFixedFastMultiply(x, y) (((x) * (y)) >> 16)
33
34#define SPAN_BUFFER_SIZE 256
35
36#define COORD_ROUNDING 1 // 0: round up, 1: round down
37#define COORD_OFFSET 32 // 26.6, 32 is half a pixel
38
39static inline QT_FT_Vector PointToVector(const QPointF &p)
40{
41 QT_FT_Vector result = { .x: QT_FT_Pos(p.x() * 64), .y: QT_FT_Pos(p.y() * 64) };
42 return result;
43}
44
45class QSpanBuffer {
46public:
47 QSpanBuffer(ProcessSpans blend, void *data, const QRect &clipRect)
48 : m_spanCount(0)
49 , m_blend(blend)
50 , m_data(data)
51 , m_clipRect(clipRect)
52 {
53 }
54
55 ~QSpanBuffer()
56 {
57 flushSpans();
58 }
59
60 void addSpan(int x, int len, int y, int coverage)
61 {
62 if (!coverage || !len)
63 return;
64
65 Q_ASSERT(coverage >= 0 && coverage <= 255);
66 Q_ASSERT(y >= m_clipRect.top());
67 Q_ASSERT(y <= m_clipRect.bottom());
68 Q_ASSERT(x >= m_clipRect.left());
69 Q_ASSERT(x + int(len) - 1 <= m_clipRect.right());
70
71 m_spans[m_spanCount].x = x;
72 m_spans[m_spanCount].len = len;
73 m_spans[m_spanCount].y = y;
74 m_spans[m_spanCount].coverage = coverage;
75
76 if (++m_spanCount == SPAN_BUFFER_SIZE)
77 flushSpans();
78 }
79
80private:
81 void flushSpans()
82 {
83 m_blend(m_spanCount, m_spans, m_data);
84 m_spanCount = 0;
85 }
86
87 QT_FT_Span m_spans[SPAN_BUFFER_SIZE];
88 int m_spanCount;
89
90 ProcessSpans m_blend;
91 void *m_data;
92
93 QRect m_clipRect;
94};
95
96#define CHUNK_SIZE 64
97class QScanConverter
98{
99public:
100 QScanConverter();
101 ~QScanConverter();
102
103 void begin(int top, int bottom, int left, int right,
104 Qt::FillRule fillRule, QSpanBuffer *spanBuffer);
105 void end();
106
107 void mergeCurve(const QT_FT_Vector &a, const QT_FT_Vector &b,
108 const QT_FT_Vector &c, const QT_FT_Vector &d);
109 void mergeLine(QT_FT_Vector a, QT_FT_Vector b);
110
111 struct Line
112 {
113 QScFixed x;
114 QScFixed delta;
115
116 int top, bottom;
117
118 int winding;
119 };
120
121private:
122 struct Intersection
123 {
124 int x;
125 int winding;
126
127 int left, right;
128 };
129
130 inline bool clip(QScFixed &xFP, int &iTop, int &iBottom, QScFixed slopeFP, QScFixed edgeFP, int winding);
131 inline void mergeIntersection(Intersection *head, const Intersection &isect);
132
133 void prepareChunk();
134
135 void emitNode(const Intersection *node);
136 void emitSpans(int chunk);
137
138 inline void allocate(int size);
139
140 QDataBuffer<Line> m_lines;
141
142 int m_alloc;
143 int m_size;
144
145 int m_top;
146 int m_bottom;
147
148 QScFixed m_leftFP;
149 QScFixed m_rightFP;
150
151 int m_fillRuleMask;
152
153 int m_x;
154 int m_y;
155 int m_winding;
156
157 Intersection *m_intersections;
158
159 QSpanBuffer *m_spanBuffer;
160
161 QDataBuffer<Line *> m_active;
162
163 template <bool AllVertical>
164 void scanConvert();
165};
166
167class QRasterizerPrivate
168{
169public:
170 bool antialiased;
171 ProcessSpans blend;
172 void *data;
173 QRect clipRect;
174
175 QScanConverter scanConverter;
176};
177
178QScanConverter::QScanConverter()
179 : m_lines(0)
180 , m_alloc(0)
181 , m_size(0)
182 , m_intersections(nullptr)
183 , m_active(0)
184{
185}
186
187QScanConverter::~QScanConverter()
188{
189 if (m_intersections)
190 free(ptr: m_intersections);
191}
192
193void QScanConverter::begin(int top, int bottom, int left, int right,
194 Qt::FillRule fillRule,
195 QSpanBuffer *spanBuffer)
196{
197 m_top = top;
198 m_bottom = bottom;
199 m_leftFP = IntToQScFixed(left);
200 m_rightFP = IntToQScFixed(right + 1);
201
202 m_lines.reset();
203
204 m_fillRuleMask = fillRule == Qt::WindingFill ? ~0x0 : 0x1;
205 m_spanBuffer = spanBuffer;
206}
207
208void QScanConverter::prepareChunk()
209{
210 m_size = CHUNK_SIZE;
211
212 allocate(CHUNK_SIZE);
213 memset(s: m_intersections, c: 0, CHUNK_SIZE * sizeof(Intersection));
214}
215
216void QScanConverter::emitNode(const Intersection *node)
217{
218tail_call:
219 if (node->left)
220 emitNode(node: node + node->left);
221
222 if (m_winding & m_fillRuleMask)
223 m_spanBuffer->addSpan(x: m_x, len: node->x - m_x, y: m_y, coverage: 0xff);
224
225 m_x = node->x;
226 m_winding += node->winding;
227
228 if (node->right) {
229 node += node->right;
230 goto tail_call;
231 }
232}
233
234void QScanConverter::emitSpans(int chunk)
235{
236 for (int dy = 0; dy < CHUNK_SIZE; ++dy) {
237 m_x = 0;
238 m_y = chunk + dy;
239 m_winding = 0;
240
241 emitNode(node: &m_intersections[dy]);
242 }
243}
244
245// split control points b[0] ... b[3] into
246// left (b[0] ... b[3]) and right (b[3] ... b[6])
247static void split(QT_FT_Vector *b)
248{
249 b[6] = b[3];
250
251 {
252 const QT_FT_Pos temp = (b[1].x + b[2].x)/2;
253
254 b[1].x = (b[0].x + b[1].x)/2;
255 b[5].x = (b[2].x + b[3].x)/2;
256 b[2].x = (b[1].x + temp)/2;
257 b[4].x = (b[5].x + temp)/2;
258 b[3].x = (b[2].x + b[4].x)/2;
259 }
260 {
261 const QT_FT_Pos temp = (b[1].y + b[2].y)/2;
262
263 b[1].y = (b[0].y + b[1].y)/2;
264 b[5].y = (b[2].y + b[3].y)/2;
265 b[2].y = (b[1].y + temp)/2;
266 b[4].y = (b[5].y + temp)/2;
267 b[3].y = (b[2].y + b[4].y)/2;
268 }
269}
270
271template <bool AllVertical>
272void QScanConverter::scanConvert()
273{
274 if (!m_lines.size()) {
275 m_active.reset();
276 return;
277 }
278 constexpr auto topOrder = [](const Line &a, const Line &b) {
279 return a.top < b.top;
280 };
281 constexpr auto xOrder = [](const Line *a, const Line *b) {
282 return a->x < b->x;
283 };
284
285 std::sort(m_lines.data(), m_lines.data() + m_lines.size(), topOrder);
286 int line = 0;
287 for (int y = m_lines.first().top; y <= m_bottom; ++y) {
288 for (; line < m_lines.size() && m_lines.at(i: line).top == y; ++line) {
289 // add node to active list
290 if constexpr(AllVertical) {
291 QScanConverter::Line *l = &m_lines.at(i: line);
292 m_active.resize(size: m_active.size() + 1);
293 int j;
294 for (j = m_active.size() - 2; j >= 0 && xOrder(l, m_active.at(i: j)); --j)
295 m_active.at(i: j+1) = m_active.at(i: j);
296 m_active.at(i: j+1) = l;
297 } else {
298 m_active << &m_lines.at(i: line);
299 }
300 }
301
302 int numActive = m_active.size();
303 if constexpr(!AllVertical) {
304 // use insertion sort instead of std::sort, as the active edge list is quite small
305 // and in the average case already sorted
306 for (int i = 1; i < numActive; ++i) {
307 QScanConverter::Line *l = m_active.at(i);
308 int j;
309 for (j = i-1; j >= 0 && xOrder(l, m_active.at(i: j)); --j)
310 m_active.at(i: j+1) = m_active.at(i: j);
311 m_active.at(i: j+1) = l;
312 }
313 }
314
315 int x = 0;
316 int winding = 0;
317 for (int i = 0; i < numActive; ++i) {
318 QScanConverter::Line *node = m_active.at(i);
319
320 const int current = QScFixedToInt(node->x);
321 if (winding & m_fillRuleMask)
322 m_spanBuffer->addSpan(x, len: current - x, y, coverage: 0xff);
323
324 x = current;
325 winding += node->winding;
326
327 if (node->bottom == y) {
328 // remove node from active list
329 for (int j = i; j < numActive - 1; ++j)
330 m_active.at(i: j) = m_active.at(i: j+1);
331
332 m_active.resize(size: --numActive);
333 --i;
334 } else {
335 if constexpr(!AllVertical)
336 node->x += node->delta;
337 }
338 }
339 }
340 m_active.reset();
341}
342
343void QScanConverter::end()
344{
345 if (m_lines.isEmpty())
346 return;
347
348 if (m_lines.size() <= 32) {
349 bool allVertical = true;
350 for (int i = 0; i < m_lines.size(); ++i) {
351 if (m_lines.at(i).delta) {
352 allVertical = false;
353 break;
354 }
355 }
356 if (allVertical)
357 scanConvert<true>();
358 else
359 scanConvert<false>();
360 } else {
361 for (int chunkTop = m_top; chunkTop <= m_bottom; chunkTop += CHUNK_SIZE) {
362 prepareChunk();
363
364 Intersection isect = { .x: 0, .winding: 0, .left: 0, .right: 0 };
365
366 const int chunkBottom = chunkTop + CHUNK_SIZE;
367 for (int i = 0; i < m_lines.size(); ++i) {
368 Line &line = m_lines.at(i);
369
370 if ((line.bottom < chunkTop) || (line.top > chunkBottom))
371 continue;
372
373 const int top = qMax(a: 0, b: line.top - chunkTop);
374 const int bottom = qMin(CHUNK_SIZE, b: line.bottom + 1 - chunkTop);
375 allocate(size: m_size + bottom - top);
376
377 isect.winding = line.winding;
378
379 Intersection *it = m_intersections + top;
380 Intersection *end = m_intersections + bottom;
381
382 if (line.delta) {
383 for (; it != end; ++it) {
384 isect.x = QScFixedToInt(line.x);
385 line.x += line.delta;
386 mergeIntersection(head: it, isect);
387 }
388 } else {
389 isect.x = QScFixedToInt(line.x);
390 for (; it != end; ++it)
391 mergeIntersection(head: it, isect);
392 }
393 }
394
395 emitSpans(chunk: chunkTop);
396 }
397 }
398
399 if (m_alloc > 1024) {
400 free(ptr: m_intersections);
401 m_alloc = 0;
402 m_size = 0;
403 m_intersections = nullptr;
404 }
405
406 if (m_lines.size() > 1024)
407 m_lines.shrink(size: 1024);
408}
409
410inline void QScanConverter::allocate(int size)
411{
412 if (m_alloc < size) {
413 int newAlloc = qMax(a: size, b: 2 * m_alloc);
414 m_intersections = q_check_ptr(p: (Intersection *)realloc(ptr: m_intersections, size: newAlloc * sizeof(Intersection)));
415 m_alloc = newAlloc;
416 }
417}
418
419inline void QScanConverter::mergeIntersection(Intersection *it, const Intersection &isect)
420{
421 Intersection *current = it;
422
423 while (isect.x != current->x) {
424 int &next = isect.x < current->x ? current->left : current->right;
425 if (next)
426 current += next;
427 else {
428 Intersection *last = m_intersections + m_size;
429 next = last - current;
430 *last = isect;
431 ++m_size;
432 return;
433 }
434 }
435
436 current->winding += isect.winding;
437}
438
439void QScanConverter::mergeCurve(const QT_FT_Vector &pa, const QT_FT_Vector &pb,
440 const QT_FT_Vector &pc, const QT_FT_Vector &pd)
441{
442 // make room for 32 splits
443 QT_FT_Vector beziers[4 + 3 * 32];
444
445 QT_FT_Vector *b = beziers;
446
447 b[0] = pa;
448 b[1] = pb;
449 b[2] = pc;
450 b[3] = pd;
451
452 const QT_FT_Pos flatness = 16;
453
454 while (b >= beziers) {
455 QT_FT_Vector delta = { .x: b[3].x - b[0].x, .y: b[3].y - b[0].y };
456 QT_FT_Pos l = qAbs(t: delta.x) + qAbs(t: delta.y);
457
458 bool belowThreshold;
459 if (l > 64) {
460 qlonglong d2 = qAbs(t: qlonglong(b[1].x-b[0].x) * qlonglong(delta.y) -
461 qlonglong(b[1].y-b[0].y) * qlonglong(delta.x));
462 qlonglong d3 = qAbs(t: qlonglong(b[2].x-b[0].x) * qlonglong(delta.y) -
463 qlonglong(b[2].y-b[0].y) * qlonglong(delta.x));
464
465 qlonglong d = d2 + d3;
466
467 belowThreshold = (d <= qlonglong(flatness) * qlonglong(l));
468 } else {
469 QT_FT_Pos d = qAbs(t: b[0].x-b[1].x) + qAbs(t: b[0].y-b[1].y) +
470 qAbs(t: b[0].x-b[2].x) + qAbs(t: b[0].y-b[2].y);
471
472 belowThreshold = (d <= flatness);
473 }
474
475 if (belowThreshold || b == beziers + 3 * 32) {
476 mergeLine(a: b[0], b: b[3]);
477 b -= 3;
478 continue;
479 }
480
481 split(b);
482 b += 3;
483 }
484}
485
486inline bool QScanConverter::clip(QScFixed &xFP, int &iTop, int &iBottom, QScFixed slopeFP, QScFixed edgeFP, int winding)
487{
488 bool right = edgeFP == m_rightFP;
489
490 if (xFP == edgeFP) {
491 if ((slopeFP > 0) ^ right)
492 return false;
493 else {
494 Line line = { .x: edgeFP, .delta: 0, .top: iTop, .bottom: iBottom, .winding: winding };
495 m_lines.add(t: line);
496 return true;
497 }
498 }
499
500 QScFixed lastFP = xFP + slopeFP * (iBottom - iTop);
501
502 if (lastFP == edgeFP) {
503 if ((slopeFP < 0) ^ right)
504 return false;
505 else {
506 Line line = { .x: edgeFP, .delta: 0, .top: iTop, .bottom: iBottom, .winding: winding };
507 m_lines.add(t: line);
508 return true;
509 }
510 }
511
512 // does line cross edge?
513 if ((lastFP < edgeFP) ^ (xFP < edgeFP)) {
514 QScFixed deltaY = QScFixed((edgeFP - xFP) / QScFixedToFloat(slopeFP));
515
516 if ((xFP < edgeFP) ^ right) {
517 // top segment needs to be clipped
518 int iHeight = QScFixedToInt(deltaY + 1);
519 int iMiddle = iTop + iHeight;
520
521 Line line = { .x: edgeFP, .delta: 0, .top: iTop, .bottom: iMiddle, .winding: winding };
522 m_lines.add(t: line);
523
524 if (iMiddle != iBottom) {
525 xFP += slopeFP * (iHeight + 1);
526 iTop = iMiddle + 1;
527 } else
528 return true;
529 } else {
530 // bottom segment needs to be clipped
531 int iHeight = QScFixedToInt(deltaY);
532 int iMiddle = iTop + iHeight;
533
534 if (iMiddle != iBottom) {
535 Line line = { .x: edgeFP, .delta: 0, .top: iMiddle + 1, .bottom: iBottom, .winding: winding };
536 m_lines.add(t: line);
537
538 iBottom = iMiddle;
539 }
540 }
541 return false;
542 } else if ((xFP < edgeFP) ^ right) {
543 Line line = { .x: edgeFP, .delta: 0, .top: iTop, .bottom: iBottom, .winding: winding };
544 m_lines.add(t: line);
545 return true;
546 }
547
548 return false;
549}
550
551void QScanConverter::mergeLine(QT_FT_Vector a, QT_FT_Vector b)
552{
553 int winding = 1;
554
555 if (a.y > b.y) {
556 qSwap(value1&: a, value2&: b);
557 winding = -1;
558 }
559
560 int iTop = qMax(a: m_top, b: int((a.y + 32) >> 6));
561 int iBottom = qMin(a: m_bottom, b: int((b.y - 32) >> 6));
562
563 if (iTop <= iBottom) {
564 QScFixed aFP = QScFixedFactor/2 + FTPosToQScFixed(a.x);
565
566 if (b.x == a.x) {
567 Line line = { .x: qBound(min: m_leftFP, val: aFP, max: m_rightFP), .delta: 0, .top: iTop, .bottom: iBottom, .winding: winding };
568 m_lines.add(t: line);
569 } else {
570 const qreal slope = (b.x - a.x) / qreal(b.y - a.y);
571
572 const QScFixed slopeFP = FloatToQScFixed(slope);
573
574 QScFixed xFP = aFP + QScFixedMultiply(slopeFP,
575 IntToQScFixed(iTop)
576 + QScFixedFactor/2 - FTPosToQScFixed(a.y));
577
578 if (clip(xFP, iTop, iBottom, slopeFP, edgeFP: m_leftFP, winding))
579 return;
580
581 if (clip(xFP, iTop, iBottom, slopeFP, edgeFP: m_rightFP, winding))
582 return;
583
584 Q_ASSERT(xFP >= m_leftFP);
585
586 Line line = { .x: xFP, .delta: slopeFP, .top: iTop, .bottom: iBottom, .winding: winding };
587 m_lines.add(t: line);
588 }
589 }
590}
591
592QRasterizer::QRasterizer()
593 : d(new QRasterizerPrivate)
594{
595}
596
597QRasterizer::~QRasterizer()
598{
599 delete d;
600}
601
602void QRasterizer::setAntialiased(bool antialiased)
603{
604 d->antialiased = antialiased;
605}
606
607void QRasterizer::initialize(ProcessSpans blend, void *data)
608{
609 d->blend = blend;
610 d->data = data;
611}
612
613void QRasterizer::setClipRect(const QRect &clipRect)
614{
615 d->clipRect = clipRect;
616}
617
618static QScFixed intersectPixelFP(int x, QScFixed top, QScFixed bottom, QScFixed leftIntersectX, QScFixed rightIntersectX, QScFixed slope, QScFixed invSlope)
619{
620 QScFixed leftX = IntToQScFixed(x);
621 QScFixed rightX = IntToQScFixed(x) + QScFixedFactor;
622
623 QScFixed leftIntersectY, rightIntersectY;
624 auto computeIntersectY = [&]() {
625 if (slope > 0) {
626 leftIntersectY = top + QScFixedMultiply(leftX - leftIntersectX, invSlope);
627 rightIntersectY = leftIntersectY + invSlope;
628 } else {
629 leftIntersectY = top + QScFixedMultiply(leftX - rightIntersectX, invSlope);
630 rightIntersectY = leftIntersectY + invSlope;
631 }
632 };
633
634 if (leftIntersectX >= leftX && rightIntersectX <= rightX) {
635 return QScFixedMultiply(bottom - top, leftIntersectX - leftX + ((rightIntersectX - leftIntersectX) >> 1));
636 } else if (leftIntersectX >= rightX) {
637 return bottom - top;
638 } else if (leftIntersectX >= leftX) {
639 computeIntersectY();
640 if (slope > 0) {
641 return (bottom - top) - QScFixedFastMultiply((rightX - leftIntersectX) >> 1, rightIntersectY - top);
642 } else {
643 return (bottom - top) - QScFixedFastMultiply((rightX - leftIntersectX) >> 1, bottom - rightIntersectY);
644 }
645 } else if (rightIntersectX <= leftX) {
646 return 0;
647 } else if (rightIntersectX <= rightX) {
648 computeIntersectY();
649 if (slope > 0) {
650 return QScFixedFastMultiply((rightIntersectX - leftX) >> 1, bottom - leftIntersectY);
651 } else {
652 return QScFixedFastMultiply((rightIntersectX - leftX) >> 1, leftIntersectY - top);
653 }
654 } else {
655 computeIntersectY();
656 if (slope > 0) {
657 return (bottom - rightIntersectY) + ((rightIntersectY - leftIntersectY) >> 1);
658 } else {
659 return (rightIntersectY - top) + ((leftIntersectY - rightIntersectY) >> 1);
660 }
661 }
662}
663
664static inline bool q26Dot6Compare(qreal p1, qreal p2)
665{
666 return int((p2 - p1) * 64.) == 0;
667}
668
669static inline QPointF snapTo26Dot6Grid(const QPointF &p)
670{
671 return QPointF(std::floor(x: p.x() * 64) * (1 / qreal(64)),
672 std::floor(x: p.y() * 64) * (1 / qreal(64)));
673}
674
675/*
676 The rasterize line function relies on some div by zero which should
677 result in +/-inf values. However, when floating point exceptions are
678 enabled, this will cause crashes, so we return high numbers instead.
679 As the returned value is used in further arithmetic, returning
680 FLT_MAX/DBL_MAX will also cause values, so instead return a value
681 that is well outside the int-range.
682 */
683static inline qreal qSafeDivide(qreal x, qreal y)
684{
685 if (y == 0)
686 return x > 0 ? 1e20 : -1e20;
687 return x / y;
688}
689
690/* Conversion to int fails if the value is too large to fit into INT_MAX or
691 too small to fit into INT_MIN, so we need this slightly safer conversion
692 when floating point exceptions are enabled
693 */
694static inline QScFixed qSafeFloatToQScFixed(qreal x)
695{
696 qreal tmp = x * QScFixedFactor;
697#if Q_PROCESSOR_WORDSIZE == 8
698 if (tmp > qreal(INT_MAX) * QScFixedFactor)
699 return QScFixed(INT_MAX) * QScFixedFactor;
700 else if (tmp < qreal(INT_MIN) * QScFixedFactor)
701 return QScFixed(INT_MIN) * QScFixedFactor;
702#else
703 if (tmp > qreal(INT_MAX))
704 return INT_MAX;
705 else if (tmp < qreal(INT_MIN))
706 return -INT_MAX;
707#endif
708 return QScFixed(tmp);
709}
710
711// returns true if the whole line gets clipped away
712static inline bool qClipLine(QPointF *pt1, QPointF *pt2, const QRectF &clip)
713{
714 qreal &x1 = pt1->rx();
715 qreal &y1 = pt1->ry();
716 qreal &x2 = pt2->rx();
717 qreal &y2 = pt2->ry();
718
719 if (!qIsFinite(d: x1) || !qIsFinite(d: y1) || !qIsFinite(d: x2) || !qIsFinite(d: y2))
720 return true;
721
722 const qreal xmin = clip.left();
723 const qreal xmax = clip.right();
724 const qreal ymin = clip.top();
725 const qreal ymax = clip.bottom();
726
727 if (x1 < xmin) {
728 if (x2 <= xmin)
729 return true;
730 y1 += (y2 - y1) / (x2 - x1) * (xmin - x1);
731 x1 = xmin;
732 } else if (x1 > xmax) {
733 if (x2 >= xmax)
734 return true;
735 y1 += (y2 - y1) / (x2 - x1) * (xmax - x1);
736 x1 = xmax;
737 }
738 if (x2 < xmin) {
739 y2 += (y2 - y1) / (x2 - x1) * (xmin - x2);
740 x2 = xmin;
741 } else if (x2 > xmax) {
742 y2 += (y2 - y1) / (x2 - x1) * (xmax - x2);
743 x2 = xmax;
744 }
745
746 if (y1 < ymin) {
747 if (y2 <= ymin)
748 return true;
749 x1 += (x2 - x1) / (y2 - y1) * (ymin - y1);
750 y1 = ymin;
751 } else if (y1 > ymax) {
752 if (y2 >= ymax)
753 return true;
754 x1 += (x2 - x1) / (y2 - y1) * (ymax - y1);
755 y1 = ymax;
756 }
757 if (y2 < ymin) {
758 x2 += (x2 - x1) / (y2 - y1) * (ymin - y2);
759 y2 = ymin;
760 } else if (y2 > ymax) {
761 x2 += (x2 - x1) / (y2 - y1) * (ymax - y2);
762 y2 = ymax;
763 }
764
765 return false;
766}
767
768void QRasterizer::rasterizeLine(const QPointF &a, const QPointF &b, qreal width, bool squareCap)
769{
770 if (a == b || !(width > 0.0) || d->clipRect.isEmpty())
771 return;
772
773 QPointF pa = a;
774 QPointF pb = b;
775
776 if (squareCap) {
777 QPointF delta = pb - pa;
778 pa -= (0.5f * width) * delta;
779 pb += (0.5f * width) * delta;
780 }
781
782 QPointF offs = QPointF(qAbs(t: b.y() - a.y()), qAbs(t: b.x() - a.x())) * width * 0.5;
783 const QRectF clip(d->clipRect.topLeft() - offs, d->clipRect.bottomRight() + QPoint(1, 1) + offs);
784 if (qClipLine(pt1: &pa, pt2: &pb, clip))
785 return;
786
787 {
788 // old delta
789 const QPointF d0 = a - b;
790 const qreal w0 = d0.x() * d0.x() + d0.y() * d0.y();
791
792 // new delta
793 const QPointF d = pa - pb;
794 const qreal w = d.x() * d.x() + d.y() * d.y();
795
796 if (w == 0)
797 return;
798
799 // adjust width which is given relative to |b - a|
800 width *= qSqrt(v: w0 / w);
801 }
802
803 QSpanBuffer buffer(d->blend, d->data, d->clipRect);
804
805 if (q26Dot6Compare(p1: pa.y(), p2: pb.y())) {
806 const qreal x = (pa.x() + pb.x()) * 0.5f;
807 const qreal dx = qAbs(t: pb.x() - pa.x()) * 0.5f;
808
809 const qreal y = pa.y();
810 const qreal dy = width * dx;
811
812 pa = QPointF(x, y - dy);
813 pb = QPointF(x, y + dy);
814
815 width = 1 / width;
816 }
817
818 if (q26Dot6Compare(p1: pa.x(), p2: pb.x())) {
819 if (pa.y() > pb.y())
820 qSwap(value1&: pa, value2&: pb);
821
822 const qreal dy = pb.y() - pa.y();
823 const qreal halfWidth = 0.5f * width * dy;
824
825 qreal left = pa.x() - halfWidth;
826 qreal right = pa.x() + halfWidth;
827
828 left = qBound(min: qreal(d->clipRect.left()), val: left, max: qreal(d->clipRect.right() + 1));
829 right = qBound(min: qreal(d->clipRect.left()), val: right, max: qreal(d->clipRect.right() + 1));
830
831 pa.ry() = qBound(min: qreal(d->clipRect.top()), val: pa.y(), max: qreal(d->clipRect.bottom() + 1));
832 pb.ry() = qBound(min: qreal(d->clipRect.top()), val: pb.y(), max: qreal(d->clipRect.bottom() + 1));
833
834 if (q26Dot6Compare(p1: left, p2: right) || q26Dot6Compare(p1: pa.y(), p2: pb.y()))
835 return;
836
837 if (d->antialiased) {
838 const int iLeft = int(left);
839 const int iRight = int(right);
840 const QScFixed leftWidth = IntToQScFixed(iLeft + 1)
841 - qSafeFloatToQScFixed(x: left);
842 const QScFixed rightWidth = qSafeFloatToQScFixed(x: right)
843 - IntToQScFixed(iRight);
844
845 QScFixed coverage[3];
846 int x[3];
847 int len[3];
848
849 int n = 1;
850 if (iLeft == iRight) {
851 if (leftWidth == QScFixedFactor)
852 coverage[0] = rightWidth * 255;
853 else
854 coverage[0] = (rightWidth + leftWidth - QScFixedFactor) * 255;
855 x[0] = iLeft;
856 len[0] = 1;
857 } else {
858 coverage[0] = leftWidth * 255;
859 x[0] = iLeft;
860 len[0] = 1;
861 if (leftWidth == QScFixedFactor) {
862 len[0] = iRight - iLeft;
863 } else if (iRight - iLeft > 1) {
864 coverage[1] = IntToQScFixed(255);
865 x[1] = iLeft + 1;
866 len[1] = iRight - iLeft - 1;
867 ++n;
868 }
869 if (rightWidth) {
870 coverage[n] = rightWidth * 255;
871 x[n] = iRight;
872 len[n] = 1;
873 ++n;
874 }
875 }
876
877 const QScFixed iTopFP = IntToQScFixed(int(pa.y()));
878 const QScFixed iBottomFP = IntToQScFixed(int(pb.y()));
879 const QScFixed yPa = qSafeFloatToQScFixed(x: pa.y());
880 const QScFixed yPb = qSafeFloatToQScFixed(x: pb.y());
881 for (QScFixed yFP = iTopFP; yFP <= iBottomFP; yFP += QScFixedFactor) {
882 const QScFixed rowHeight = qMin(a: yFP + QScFixedFactor, b: yPb)
883 - qMax(a: yFP, b: yPa);
884 const int y = QScFixedToInt(yFP);
885 if (y > d->clipRect.bottom())
886 break;
887 for (int i = 0; i < n; ++i) {
888 buffer.addSpan(x: x[i], len: len[i], y,
889 QScFixedToInt(QScFixedMultiply(rowHeight, coverage[i])));
890 }
891 }
892 } else { // aliased
893 int iTop = int(pa.y() + 0.5f);
894 int iBottom = pb.y() < 0.5f ? -1 : int(pb.y() - 0.5f);
895 int iLeft = int(left + 0.5f);
896 int iRight = right < 0.5f ? -1 : int(right - 0.5f);
897
898 int iWidth = iRight - iLeft + 1;
899 for (int y = iTop; y <= iBottom; ++y)
900 buffer.addSpan(x: iLeft, len: iWidth, y, coverage: 255);
901 }
902 } else {
903 if (pa.y() > pb.y())
904 qSwap(value1&: pa, value2&: pb);
905
906 QPointF delta = pb - pa;
907 delta *= 0.5f * width;
908 const QPointF perp(delta.y(), -delta.x());
909
910 QPointF top;
911 QPointF left;
912 QPointF right;
913 QPointF bottom;
914
915 if (pa.x() < pb.x()) {
916 top = pa + perp;
917 left = pa - perp;
918 right = pb + perp;
919 bottom = pb - perp;
920 } else {
921 top = pa - perp;
922 left = pb - perp;
923 right = pa + perp;
924 bottom = pb + perp;
925 }
926
927 top = snapTo26Dot6Grid(p: top);
928 bottom = snapTo26Dot6Grid(p: bottom);
929 left = snapTo26Dot6Grid(p: left);
930 right = snapTo26Dot6Grid(p: right);
931
932 const qreal topBound = qBound(min: qreal(d->clipRect.top()), val: top.y(), max: qreal(d->clipRect.bottom()));
933 const qreal bottomBound = qBound(min: qreal(d->clipRect.top()), val: bottom.y(), max: qreal(d->clipRect.bottom()));
934
935 const QPointF topLeftEdge = left - top;
936 const QPointF topRightEdge = right - top;
937 const QPointF bottomLeftEdge = bottom - left;
938 const QPointF bottomRightEdge = bottom - right;
939
940 const qreal topLeftSlope = qSafeDivide(x: topLeftEdge.x(), y: topLeftEdge.y());
941 const qreal bottomLeftSlope = qSafeDivide(x: bottomLeftEdge.x(), y: bottomLeftEdge.y());
942
943 const qreal topRightSlope = qSafeDivide(x: topRightEdge.x(), y: topRightEdge.y());
944 const qreal bottomRightSlope = qSafeDivide(x: bottomRightEdge.x(), y: bottomRightEdge.y());
945
946 const QScFixed topLeftSlopeFP = qSafeFloatToQScFixed(x: topLeftSlope);
947 const QScFixed topRightSlopeFP = qSafeFloatToQScFixed(x: topRightSlope);
948
949 const QScFixed bottomLeftSlopeFP = qSafeFloatToQScFixed(x: bottomLeftSlope);
950 const QScFixed bottomRightSlopeFP = qSafeFloatToQScFixed(x: bottomRightSlope);
951
952 const QScFixed invTopLeftSlopeFP = qSafeFloatToQScFixed(x: qSafeDivide(x: 1, y: topLeftSlope));
953 const QScFixed invTopRightSlopeFP = qSafeFloatToQScFixed(x: qSafeDivide(x: 1, y: topRightSlope));
954
955 const QScFixed invBottomLeftSlopeFP = qSafeFloatToQScFixed(x: qSafeDivide(x: 1, y: bottomLeftSlope));
956 const QScFixed invBottomRightSlopeFP = qSafeFloatToQScFixed(x: qSafeDivide(x: 1, y: bottomRightSlope));
957
958 if (d->antialiased) {
959 const QScFixed iTopFP = IntToQScFixed(int(topBound));
960 const QScFixed iLeftFP = IntToQScFixed(int(left.y()));
961 const QScFixed iRightFP = IntToQScFixed(int(right.y()));
962 const QScFixed iBottomFP = IntToQScFixed(int(bottomBound));
963
964 QScFixed leftIntersectAf = qSafeFloatToQScFixed(x: top.x() + (int(topBound) - top.y()) * topLeftSlope);
965 QScFixed rightIntersectAf = qSafeFloatToQScFixed(x: top.x() + (int(topBound) - top.y()) * topRightSlope);
966 QScFixed leftIntersectBf = 0;
967 QScFixed rightIntersectBf = 0;
968
969 if (iLeftFP < iTopFP)
970 leftIntersectBf = qSafeFloatToQScFixed(x: left.x() + (int(topBound) - left.y()) * bottomLeftSlope);
971
972 if (iRightFP < iTopFP)
973 rightIntersectBf = qSafeFloatToQScFixed(x: right.x() + (int(topBound) - right.y()) * bottomRightSlope);
974
975 QScFixed rowTop, rowBottomLeft, rowBottomRight, rowTopLeft, rowTopRight, rowBottom;
976 QScFixed topLeftIntersectAf, topLeftIntersectBf, topRightIntersectAf, topRightIntersectBf;
977 QScFixed bottomLeftIntersectAf, bottomLeftIntersectBf, bottomRightIntersectAf, bottomRightIntersectBf;
978
979 int leftMin, leftMax, rightMin, rightMax;
980
981 const QScFixed yTopFP = qSafeFloatToQScFixed(x: top.y());
982 const QScFixed yLeftFP = qSafeFloatToQScFixed(x: left.y());
983 const QScFixed yRightFP = qSafeFloatToQScFixed(x: right.y());
984 const QScFixed yBottomFP = qSafeFloatToQScFixed(x: bottom.y());
985
986 rowTop = qMax(a: iTopFP, b: yTopFP);
987 topLeftIntersectAf = leftIntersectAf +
988 QScFixedMultiply(topLeftSlopeFP, rowTop - iTopFP);
989 topRightIntersectAf = rightIntersectAf +
990 QScFixedMultiply(topRightSlopeFP, rowTop - iTopFP);
991
992 QScFixed yFP = iTopFP;
993 while (yFP <= iBottomFP) {
994 rowBottomLeft = qMin(a: yFP + QScFixedFactor, b: yLeftFP);
995 rowBottomRight = qMin(a: yFP + QScFixedFactor, b: yRightFP);
996 rowTopLeft = qMax(a: yFP, b: yLeftFP);
997 rowTopRight = qMax(a: yFP, b: yRightFP);
998 rowBottom = qMin(a: yFP + QScFixedFactor, b: yBottomFP);
999
1000 if (yFP == iLeftFP) {
1001 const int y = QScFixedToInt(yFP);
1002 leftIntersectBf = qSafeFloatToQScFixed(x: left.x() + (y - left.y()) * bottomLeftSlope);
1003 topLeftIntersectBf = leftIntersectBf + QScFixedMultiply(bottomLeftSlopeFP, rowTopLeft - yFP);
1004 bottomLeftIntersectAf = leftIntersectAf + QScFixedMultiply(topLeftSlopeFP, rowBottomLeft - yFP);
1005 } else {
1006 topLeftIntersectBf = leftIntersectBf;
1007 bottomLeftIntersectAf = leftIntersectAf + topLeftSlopeFP;
1008 }
1009
1010 if (yFP == iRightFP) {
1011 const int y = QScFixedToInt(yFP);
1012 rightIntersectBf = qSafeFloatToQScFixed(x: right.x() + (y - right.y()) * bottomRightSlope);
1013 topRightIntersectBf = rightIntersectBf + QScFixedMultiply(bottomRightSlopeFP, rowTopRight - yFP);
1014 bottomRightIntersectAf = rightIntersectAf + QScFixedMultiply(topRightSlopeFP, rowBottomRight - yFP);
1015 } else {
1016 topRightIntersectBf = rightIntersectBf;
1017 bottomRightIntersectAf = rightIntersectAf + topRightSlopeFP;
1018 }
1019
1020 if (yFP == iBottomFP) {
1021 bottomLeftIntersectBf = leftIntersectBf + QScFixedMultiply(bottomLeftSlopeFP, rowBottom - yFP);
1022 bottomRightIntersectBf = rightIntersectBf + QScFixedMultiply(bottomRightSlopeFP, rowBottom - yFP);
1023 } else {
1024 bottomLeftIntersectBf = leftIntersectBf + bottomLeftSlopeFP;
1025 bottomRightIntersectBf = rightIntersectBf + bottomRightSlopeFP;
1026 }
1027
1028 if (yFP < iLeftFP) {
1029 leftMin = QScFixedToInt(bottomLeftIntersectAf);
1030 leftMax = QScFixedToInt(topLeftIntersectAf);
1031 } else if (yFP == iLeftFP) {
1032 leftMin = QScFixedToInt(qMax(bottomLeftIntersectAf, topLeftIntersectBf));
1033 leftMax = QScFixedToInt(qMax(topLeftIntersectAf, bottomLeftIntersectBf));
1034 } else {
1035 leftMin = QScFixedToInt(topLeftIntersectBf);
1036 leftMax = QScFixedToInt(bottomLeftIntersectBf);
1037 }
1038
1039 leftMin = qBound(min: d->clipRect.left(), val: leftMin, max: d->clipRect.right());
1040 leftMax = qBound(min: d->clipRect.left(), val: leftMax, max: d->clipRect.right());
1041
1042 if (yFP < iRightFP) {
1043 rightMin = QScFixedToInt(topRightIntersectAf);
1044 rightMax = QScFixedToInt(bottomRightIntersectAf);
1045 } else if (yFP == iRightFP) {
1046 rightMin = QScFixedToInt(qMin(topRightIntersectAf, bottomRightIntersectBf));
1047 rightMax = QScFixedToInt(qMin(bottomRightIntersectAf, topRightIntersectBf));
1048 } else {
1049 rightMin = QScFixedToInt(bottomRightIntersectBf);
1050 rightMax = QScFixedToInt(topRightIntersectBf);
1051 }
1052
1053 rightMin = qBound(min: d->clipRect.left(), val: rightMin, max: d->clipRect.right());
1054 rightMax = qBound(min: d->clipRect.left(), val: rightMax, max: d->clipRect.right());
1055
1056 if (leftMax > rightMax)
1057 leftMax = rightMax;
1058 if (rightMin < leftMin)
1059 rightMin = leftMin;
1060
1061 QScFixed rowHeight = rowBottom - rowTop;
1062
1063 int x = leftMin;
1064 while (x <= leftMax) {
1065 QScFixed excluded = 0;
1066
1067 if (yFP <= iLeftFP && rowBottomLeft > rowTop)
1068 excluded += intersectPixelFP(x, top: rowTop, bottom: rowBottomLeft,
1069 leftIntersectX: bottomLeftIntersectAf, rightIntersectX: topLeftIntersectAf,
1070 slope: topLeftSlopeFP, invSlope: invTopLeftSlopeFP);
1071 if (yFP >= iLeftFP && rowBottom > rowTopLeft)
1072 excluded += intersectPixelFP(x, top: rowTopLeft, bottom: rowBottom,
1073 leftIntersectX: topLeftIntersectBf, rightIntersectX: bottomLeftIntersectBf,
1074 slope: bottomLeftSlopeFP, invSlope: invBottomLeftSlopeFP);
1075 if (x >= rightMin) {
1076 if (yFP <= iRightFP && rowBottomRight > rowTop)
1077 excluded += (rowBottomRight - rowTop) - intersectPixelFP(x, top: rowTop, bottom: rowBottomRight,
1078 leftIntersectX: topRightIntersectAf, rightIntersectX: bottomRightIntersectAf,
1079 slope: topRightSlopeFP, invSlope: invTopRightSlopeFP);
1080 if (yFP >= iRightFP && rowBottom > rowTopRight)
1081 excluded += (rowBottom - rowTopRight) - intersectPixelFP(x, top: rowTopRight, bottom: rowBottom,
1082 leftIntersectX: bottomRightIntersectBf, rightIntersectX: topRightIntersectBf,
1083 slope: bottomRightSlopeFP, invSlope: invBottomRightSlopeFP);
1084 }
1085
1086 Q_ASSERT(excluded >= 0 && excluded <= rowHeight);
1087 QScFixed coverage = rowHeight - excluded;
1088 buffer.addSpan(x, len: 1, QScFixedToInt(yFP),
1089 QScFixedToInt(255 * coverage));
1090 ++x;
1091 }
1092 if (x < rightMin) {
1093 buffer.addSpan(x, len: rightMin - x, QScFixedToInt(yFP),
1094 QScFixedToInt(255 * rowHeight));
1095 x = rightMin;
1096 }
1097 while (x <= rightMax) {
1098 QScFixed excluded = 0;
1099 if (yFP <= iRightFP && rowBottomRight > rowTop)
1100 excluded += (rowBottomRight - rowTop) - intersectPixelFP(x, top: rowTop, bottom: rowBottomRight,
1101 leftIntersectX: topRightIntersectAf, rightIntersectX: bottomRightIntersectAf,
1102 slope: topRightSlopeFP, invSlope: invTopRightSlopeFP);
1103 if (yFP >= iRightFP && rowBottom > rowTopRight)
1104 excluded += (rowBottom - rowTopRight) - intersectPixelFP(x, top: rowTopRight, bottom: rowBottom,
1105 leftIntersectX: bottomRightIntersectBf, rightIntersectX: topRightIntersectBf,
1106 slope: bottomRightSlopeFP, invSlope: invBottomRightSlopeFP);
1107
1108 Q_ASSERT(excluded >= 0 && excluded <= rowHeight);
1109 QScFixed coverage = rowHeight - excluded;
1110 buffer.addSpan(x, len: 1, QScFixedToInt(yFP),
1111 QScFixedToInt(255 * coverage));
1112 ++x;
1113 }
1114
1115 leftIntersectAf += topLeftSlopeFP;
1116 leftIntersectBf += bottomLeftSlopeFP;
1117 rightIntersectAf += topRightSlopeFP;
1118 rightIntersectBf += bottomRightSlopeFP;
1119 topLeftIntersectAf = leftIntersectAf;
1120 topRightIntersectAf = rightIntersectAf;
1121
1122 yFP += QScFixedFactor;
1123 rowTop = yFP;
1124 }
1125 } else { // aliased
1126 int iTop = int(top.y() + 0.5f);
1127 int iLeft = left.y() < 0.5f ? -1 : int(left.y() - 0.5f);
1128 int iRight = right.y() < 0.5f ? -1 : int(right.y() - 0.5f);
1129 int iBottom = bottom.y() < 0.5f? -1 : int(bottom.y() - 0.5f);
1130 int iMiddle = qMin(a: iLeft, b: iRight);
1131
1132 QScFixed leftIntersectAf = qSafeFloatToQScFixed(x: top.x() + 0.5f + (iTop + 0.5f - top.y()) * topLeftSlope);
1133 QScFixed leftIntersectBf = qSafeFloatToQScFixed(x: left.x() + 0.5f + (iLeft + 1.5f - left.y()) * bottomLeftSlope);
1134 QScFixed rightIntersectAf = qSafeFloatToQScFixed(x: top.x() - 0.5f + (iTop + 0.5f - top.y()) * topRightSlope);
1135 QScFixed rightIntersectBf = qSafeFloatToQScFixed(x: right.x() - 0.5f + (iRight + 1.5f - right.y()) * bottomRightSlope);
1136
1137 int ny;
1138 int y = iTop;
1139#define DO_SEGMENT(next, li, ri, ls, rs) \
1140 ny = qMin(next + 1, d->clipRect.top()); \
1141 if (y < ny) { \
1142 li += ls * (ny - y); \
1143 ri += rs * (ny - y); \
1144 y = ny; \
1145 } \
1146 if (next > d->clipRect.bottom()) \
1147 next = d->clipRect.bottom(); \
1148 for (; y <= next; ++y) { \
1149 const int x1 = qMax(QScFixedToInt(li), d->clipRect.left()); \
1150 const int x2 = qMin(QScFixedToInt(ri), d->clipRect.right()); \
1151 if (x2 >= x1) \
1152 buffer.addSpan(x1, x2 - x1 + 1, y, 255); \
1153 li += ls; \
1154 ri += rs; \
1155 }
1156
1157 DO_SEGMENT(iMiddle, leftIntersectAf, rightIntersectAf, topLeftSlopeFP, topRightSlopeFP)
1158 DO_SEGMENT(iRight, leftIntersectBf, rightIntersectAf, bottomLeftSlopeFP, topRightSlopeFP)
1159 DO_SEGMENT(iLeft, leftIntersectAf, rightIntersectBf, topLeftSlopeFP, bottomRightSlopeFP);
1160 DO_SEGMENT(iBottom, leftIntersectBf, rightIntersectBf, bottomLeftSlopeFP, bottomRightSlopeFP);
1161#undef DO_SEGMENT
1162 }
1163 }
1164}
1165
1166void QRasterizer::rasterize(const QT_FT_Outline *outline, Qt::FillRule fillRule)
1167{
1168 if (outline->n_points < 3 || outline->n_contours == 0)
1169 return;
1170
1171 const QT_FT_Vector *points = outline->points;
1172
1173 QSpanBuffer buffer(d->blend, d->data, d->clipRect);
1174
1175 // ### QT_FT_Outline already has a bounding rect which is
1176 // ### precomputed at this point, so we should probably just be
1177 // ### using that instead...
1178 QT_FT_Pos min_y = points[0].y, max_y = points[0].y;
1179 for (int i = 1; i < outline->n_points; ++i) {
1180 const QT_FT_Vector &p = points[i];
1181 min_y = qMin(a: p.y, b: min_y);
1182 max_y = qMax(a: p.y, b: max_y);
1183 }
1184
1185 int iTopBound = qMax(a: d->clipRect.top(), b: int((min_y + 32) >> 6));
1186 int iBottomBound = qMin(a: d->clipRect.bottom(), b: int((max_y - 32) >> 6));
1187
1188 if (iTopBound > iBottomBound)
1189 return;
1190
1191 d->scanConverter.begin(top: iTopBound, bottom: iBottomBound, left: d->clipRect.left(), right: d->clipRect.right(), fillRule, spanBuffer: &buffer);
1192
1193 int first = 0;
1194 for (int i = 0; i < outline->n_contours; ++i) {
1195 const int last = outline->contours[i];
1196 for (int j = first; j < last; ++j) {
1197 if (outline->tags[j+1] == QT_FT_CURVE_TAG_CUBIC) {
1198 Q_ASSERT(outline->tags[j+2] == QT_FT_CURVE_TAG_CUBIC);
1199 d->scanConverter.mergeCurve(pa: points[j], pb: points[j+1], pc: points[j+2], pd: points[j+3]);
1200 j += 2;
1201 } else {
1202 d->scanConverter.mergeLine(a: points[j], b: points[j+1]);
1203 }
1204 }
1205
1206 first = last + 1;
1207 }
1208
1209 d->scanConverter.end();
1210}
1211
1212void QRasterizer::rasterize(const QPainterPath &path, Qt::FillRule fillRule)
1213{
1214 if (path.isEmpty())
1215 return;
1216
1217 QSpanBuffer buffer(d->blend, d->data, d->clipRect);
1218
1219 QRectF bounds = path.controlPointRect();
1220
1221 int iTopBound = qMax(a: d->clipRect.top(), b: int(bounds.top() + 0.5));
1222 int iBottomBound = qMin(a: d->clipRect.bottom(), b: int(bounds.bottom() - 0.5));
1223
1224 if (iTopBound > iBottomBound)
1225 return;
1226
1227 d->scanConverter.begin(top: iTopBound, bottom: iBottomBound, left: d->clipRect.left(), right: d->clipRect.right(), fillRule, spanBuffer: &buffer);
1228
1229 int subpathStart = 0;
1230 QT_FT_Vector last = { .x: 0, .y: 0 };
1231 for (int i = 0; i < path.elementCount(); ++i) {
1232 switch (path.elementAt(i).type) {
1233 case QPainterPath::LineToElement:
1234 {
1235 QT_FT_Vector p1 = last;
1236 QT_FT_Vector p2 = PointToVector(p: path.elementAt(i));
1237 d->scanConverter.mergeLine(a: p1, b: p2);
1238 last = p2;
1239 break;
1240 }
1241 case QPainterPath::MoveToElement:
1242 {
1243 if (i != 0) {
1244 QT_FT_Vector first = PointToVector(p: path.elementAt(i: subpathStart));
1245 // close previous subpath
1246 if (first.x != last.x || first.y != last.y)
1247 d->scanConverter.mergeLine(a: last, b: first);
1248 }
1249 subpathStart = i;
1250 last = PointToVector(p: path.elementAt(i));
1251 break;
1252 }
1253 case QPainterPath::CurveToElement:
1254 {
1255 QT_FT_Vector p1 = last;
1256 QT_FT_Vector p2 = PointToVector(p: path.elementAt(i));
1257 QT_FT_Vector p3 = PointToVector(p: path.elementAt(i: ++i));
1258 QT_FT_Vector p4 = PointToVector(p: path.elementAt(i: ++i));
1259 d->scanConverter.mergeCurve(pa: p1, pb: p2, pc: p3, pd: p4);
1260 last = p4;
1261 break;
1262 }
1263 default:
1264 Q_ASSERT(false);
1265 break;
1266 }
1267 }
1268
1269 QT_FT_Vector first = PointToVector(p: path.elementAt(i: subpathStart));
1270
1271 // close path
1272 if (first.x != last.x || first.y != last.y)
1273 d->scanConverter.mergeLine(a: last, b: first);
1274
1275 d->scanConverter.end();
1276}
1277
1278QT_END_NAMESPACE
1279

source code of qtbase/src/gui/painting/qrasterizer.cpp