Coverage Report

Created: 2026-10-27 06:57

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