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