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