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