Coverage Report

Created: 2026-09-28 08:21

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/qtbase/src/gui/painting/qcosmeticstroker.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 "qcosmeticstroker_p.h"
6
#include "private/qpainterpath_p.h"
7
#include "private/qrgba64_p.h"
8
#include <qdebug.h>
9
10
QT_BEGIN_NAMESPACE
11
12
#if 0
13
inline QString capString(int caps)
14
{
15
    QString str;
16
    if (caps & QCosmeticStroker::CapBegin) {
17
        str += "CapBegin ";
18
    }
19
    if (caps & QCosmeticStroker::CapEnd) {
20
        str += "CapEnd ";
21
    }
22
    return str;
23
}
24
#endif
25
26
#if Q_PROCESSOR_WORDSIZE == 8
27
typedef qint64 FDot16;
28
#else
29
typedef int FDot16;
30
#endif
31
32
241k
#define toF26Dot6(x) static_cast<int>((x) * 64.)
33
34
static inline uint sourceOver(uint d, uint color)
35
59.9k
{
36
59.9k
    return color + BYTE_MUL(d, qAlpha(~color));
37
59.9k
}
38
39
inline static FDot16 FDot16FixedDiv(int x, int y)
40
60.2k
{
41
60.2k
#if Q_PROCESSOR_WORDSIZE == 8
42
60.2k
    return FDot16(x) * (1<<16) / y;
43
#else
44
    if (qAbs(x) > 0x7fff)
45
        return static_cast<qlonglong>(x) * (1<<16) / y;
46
    return x * (1<<16) / y;
47
#endif
48
60.2k
}
49
50
typedef void (*DrawPixel)(QCosmeticStroker *stroker, int x, int y, int coverage);
51
52
namespace {
53
54
struct Dasher {
55
    QCosmeticStroker *stroker;
56
    int *pattern;
57
    int offset;
58
    int dashIndex;
59
    int dashOn;
60
61
    Dasher(QCosmeticStroker *s, bool reverse, int start, int stop)
62
0
        : stroker(s)
63
0
    {
64
0
        int delta = stop - start;
65
0
        if (reverse) {
66
0
            pattern = stroker->reversePattern;
67
0
            offset = stroker->patternLength - stroker->patternOffset - delta - ((start & 63) - 32);
68
0
            dashOn = 0;
69
0
        } else {
70
0
            pattern = stroker->pattern;
71
0
            offset = stroker->patternOffset - ((start & 63) - 32);
72
0
            dashOn = 1;
73
0
        }
74
0
        offset %= stroker->patternLength;
75
0
        if (offset < 0)
76
0
            offset += stroker->patternLength;
77
78
0
        dashIndex = 0;
79
0
        while (dashIndex < stroker->patternSize - 1 && offset>= pattern[dashIndex])
80
0
            ++dashIndex;
81
82
//        qDebug() << "   dasher" << offset/64. << reverse << dashIndex;
83
0
        stroker->patternOffset += delta;
84
0
        stroker->patternOffset %= stroker->patternLength;
85
0
    }
86
87
0
    bool on() const {
88
0
        return (dashIndex + dashOn) & 1;
89
0
    }
90
0
    void adjust() {
91
0
        offset += 64;
92
0
        if (offset >= pattern[dashIndex]) {
93
0
            ++dashIndex;
94
0
            dashIndex %= stroker->patternSize;
95
0
        }
96
0
        offset %= stroker->patternLength;
97
//        qDebug() << "dasher.adjust" << offset/64. << dashIndex;
98
0
    }
99
};
100
101
struct NoDasher {
102
48.2k
    NoDasher(QCosmeticStroker *, bool, int, int) {}
103
60.2k
    bool on() const { return true; }
104
60.2k
    void adjust(int = 0) {}
105
};
106
107
};
108
109
/*
110
 * The return value is the result of the clipLine() call performed at the start
111
 * of each of the two functions, aka "false" means completely outside the devices
112
 * rect.
113
 */
114
template<DrawPixel drawPixel, class Dasher>
115
static bool drawLine(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps);
116
template<DrawPixel drawPixel, class Dasher>
117
static bool drawLineAA(QCosmeticStroker *stroker, qreal x1, qreal y1, qreal x2, qreal y2, int caps);
118
119
inline void drawPixel(QCosmeticStroker *stroker, int x, int y, int coverage)
120
0
{
121
0
    const QRect &cl = stroker->clip;
122
0
    if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom())
123
0
        return;
124
125
0
    const int cov = coverage * stroker->opacity >> 8;
126
127
0
    if (stroker->current_span > 0) {
128
0
        QT_FT_Span &last = stroker->spans[stroker->current_span - 1];
129
0
        const int lastx = last.x + last.len;
130
131
        // Extend the previous span instead of emitting one span per pixel
132
0
        if (y == last.y && x == lastx && cov == last.coverage) {
133
0
            ++last.len;
134
0
            return;
135
0
        }
136
137
0
        if (stroker->current_span == QCosmeticStroker::NSPANS || y < last.y || (y == last.y && x < lastx)) {
138
0
            stroker->blend(stroker->current_span, stroker->spans, &stroker->state->penData);
139
0
            stroker->current_span = 0;
140
0
        }
141
0
    }
142
143
0
    stroker->spans[stroker->current_span].x = x;
144
0
    stroker->spans[stroker->current_span].len = 1;
145
0
    stroker->spans[stroker->current_span].y = y;
146
0
    stroker->spans[stroker->current_span].coverage = cov;
147
0
    ++stroker->current_span;
148
0
}
149
150
inline void drawPixelARGB32(QCosmeticStroker *stroker, int x, int y, int coverage)
151
0
{
152
0
    const QRect &cl = stroker->clip;
153
0
    if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom())
154
0
        return;
155
156
0
    int offset = x + stroker->ppl*y;
157
0
    uint c = BYTE_MUL(stroker->color, coverage * stroker->opacity >> 8);
158
0
    stroker->pixels[offset] = sourceOver(stroker->pixels[offset], c);
159
0
}
160
161
inline void drawPixelARGB32Opaque(QCosmeticStroker *stroker, int x, int y, int)
162
60.2k
{
163
60.2k
    const QRect &cl = stroker->clip;
164
60.2k
    if (x < cl.x() || x > cl.right() || y < cl.y() || y > cl.bottom())
165
340
        return;
166
167
59.9k
    int offset = x + stroker->ppl*y;
168
59.9k
    stroker->pixels[offset] = sourceOver(stroker->pixels[offset], stroker->color);
169
59.9k
}
170
171
enum StrokeSelection {
172
    Aliased = 0,
173
    AntiAliased = 1,
174
    Solid = 0,
175
    Dashed = 2,
176
    RegularDraw = 0,
177
    FastDraw = 4
178
};
179
180
static StrokeLine strokeLine(int strokeSelection)
181
12.0k
{
182
12.0k
    StrokeLine stroke;
183
184
12.0k
    switch (strokeSelection) {
185
0
    case Aliased|Solid|RegularDraw:
186
0
        stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixel, NoDasher>;
187
0
        break;
188
12.0k
    case Aliased|Solid|FastDraw:
189
12.0k
        stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixelARGB32Opaque, NoDasher>;
190
12.0k
        break;
191
0
    case Aliased|Dashed|RegularDraw:
192
0
        stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixel, Dasher>;
193
0
        break;
194
0
    case Aliased|Dashed|FastDraw:
195
0
        stroke = &QT_PREPEND_NAMESPACE(drawLine)<drawPixelARGB32Opaque, Dasher>;
196
0
        break;
197
0
    case AntiAliased|Solid|RegularDraw:
198
0
        stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixel, NoDasher>;
199
0
        break;
200
0
    case AntiAliased|Solid|FastDraw:
201
0
        stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixelARGB32, NoDasher>;
202
0
        break;
203
0
    case AntiAliased|Dashed|RegularDraw:
204
0
        stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixel, Dasher>;
205
0
        break;
206
0
    case AntiAliased|Dashed|FastDraw:
207
0
        stroke = &QT_PREPEND_NAMESPACE(drawLineAA)<drawPixelARGB32, Dasher>;
208
0
        break;
209
0
    default:
210
0
        Q_ASSERT(false);
211
0
        stroke = nullptr;
212
12.0k
    }
213
12.0k
    return stroke;
214
12.0k
}
215
216
void QCosmeticStroker::setup()
217
12.0k
{
218
12.0k
    blend = state->penData.blend;
219
12.0k
    const QClipData *effectiveClip = state->penData.clip;
220
12.0k
    if (effectiveClip && effectiveClip->hasRectClip && !effectiveClip->clipRect.isEmpty()) {
221
12.0k
        clip &= effectiveClip->clipRect;
222
12.0k
        blend = state->penData.unclipped_blend;
223
12.0k
    }
224
225
12.0k
    int strokeSelection = 0;
226
12.0k
    if (blend == state->penData.unclipped_blend
227
12.0k
        && state->penData.type == QSpanData::Solid
228
12.0k
        && (state->penData.rasterBuffer->format == QImage::Format_ARGB32_Premultiplied
229
12.0k
            || state->penData.rasterBuffer->format == QImage::Format_RGB32)
230
12.0k
        && state->compositionMode() == QPainter::CompositionMode_SourceOver)
231
12.0k
        strokeSelection |= FastDraw;
232
233
12.0k
    if (state->renderHints & QPainter::Antialiasing)
234
0
        strokeSelection |= AntiAliased;
235
236
12.0k
    const QList<qreal> &penPattern = state->lastPen.dashPattern();
237
12.0k
    if (penPattern.isEmpty() || penPattern.size() > 1024) {
238
12.0k
        Q_ASSERT(!pattern && !reversePattern);
239
12.0k
        pattern = nullptr;
240
12.0k
        reversePattern = nullptr;
241
12.0k
        patternLength = 0;
242
12.0k
        patternSize = 0;
243
12.0k
    } else {
244
0
        pattern = static_cast<int *>(malloc(penPattern.size() * sizeof(int)));
245
0
        reversePattern = static_cast<int *>(malloc(penPattern.size() * sizeof(int)));
246
0
        patternSize = penPattern.size();
247
248
0
        patternLength = 0;
249
0
        for (int i = 0; i < patternSize; ++i) {
250
0
            patternLength += qBound(1, int(penPattern.at(i) * 64), 65536);
251
0
            pattern[i] = patternLength;
252
0
        }
253
0
        patternLength = 0;
254
0
        for (int i = 0; i < patternSize; ++i) {
255
0
            patternLength += qBound(1, int(penPattern.at(patternSize - 1 - i) * 64), 65536);
256
0
            reversePattern[i] = patternLength;
257
0
        }
258
0
        strokeSelection |= Dashed;
259
//        qDebug() << "setup: size=" << patternSize << "length=" << patternLength/64.;
260
0
    }
261
262
12.0k
    stroke = strokeLine(strokeSelection);
263
264
12.0k
    qreal width = state->lastPen.widthF();
265
12.0k
    if (width == 0)
266
0
        opacity = 256;
267
12.0k
    else if (state->lastPen.isCosmetic())
268
0
        opacity = static_cast<int>(256 * width);
269
12.0k
    else
270
12.0k
        opacity = static_cast<int>(256 * width * state->txscale);
271
12.0k
    opacity = qBound(0, opacity, 256);
272
273
12.0k
    drawCaps = state->lastPen.capStyle() != Qt::FlatCap;
274
275
12.0k
    if (strokeSelection & FastDraw) {
276
        // AA/drawPixelARGB32() does the opacity mul itself, for drawPixelARGB32Opaque() do it here:
277
12.0k
        const uint solid = state->penData.solidColor.rgba();
278
12.0k
        color = (strokeSelection & AntiAliased) ? solid : BYTE_MUL(solid, 255 * opacity >> 8);
279
12.0k
        QRasterBuffer *buffer = state->penData.rasterBuffer;
280
12.0k
        pixels = reinterpret_cast<uint *>(buffer->buffer());
281
12.0k
        ppl = buffer->stride<quint32>();
282
12.0k
    }
283
284
    // line drawing produces different results with different clips, so
285
    // we need to clip consistently when painting to the same device
286
287
    // setup FP clip bounds
288
12.0k
    xmin = deviceRect.left() - 1;
289
12.0k
    xmax = deviceRect.right() + 2;
290
12.0k
    ymin = deviceRect.top() - 1;
291
12.0k
    ymax = deviceRect.bottom() + 2;
292
293
12.0k
    lastPixel.x = INT_MIN;
294
12.0k
    lastPixel.y = INT_MIN;
295
12.0k
}
296
297
// returns true if the whole line gets clipped away
298
bool QCosmeticStroker::clipLine(qreal &x1, qreal &y1, qreal &x2, qreal &y2)
299
60.2k
{
300
60.2k
    if (!qIsFinite(x1) || !qIsFinite(y1) || !qIsFinite(x2) || !qIsFinite(y2))
301
0
        return true;
302
    // basic/rough clipping is done in floating point coordinates to avoid
303
    // integer overflow problems.
304
60.2k
    if (x1 < xmin) {
305
0
        if (x2 <= xmin)
306
0
            goto clipped;
307
0
        y1 += (y2 - y1)/(x2 - x1) * (xmin - x1);
308
0
        x1 = xmin;
309
60.2k
    } else if (x1 > xmax) {
310
0
        if (x2 >= xmax)
311
0
            goto clipped;
312
0
        y1 += (y2 - y1)/(x2 - x1) * (xmax - x1);
313
0
        x1 = xmax;
314
0
    }
315
60.2k
    if (x2 < xmin) {
316
0
        lastPixel.x = INT_MIN;
317
0
        y2 += (y2 - y1)/(x2 - x1) * (xmin - x2);
318
0
        x2 = xmin;
319
60.2k
    } else if (x2 > xmax) {
320
0
        lastPixel.x = INT_MIN;
321
0
        y2 += (y2 - y1)/(x2 - x1) * (xmax - x2);
322
0
        x2 = xmax;
323
0
    }
324
325
60.2k
    if (y1 < ymin) {
326
0
        if (y2 <= ymin)
327
0
            goto clipped;
328
0
        x1 += (x2 - x1)/(y2 - y1) * (ymin - y1);
329
0
        y1 = ymin;
330
60.2k
    } else if (y1 > ymax) {
331
0
        if (y2 >= ymax)
332
0
            goto clipped;
333
0
        x1 += (x2 - x1)/(y2 - y1) * (ymax - y1);
334
0
        y1 = ymax;
335
0
    }
336
60.2k
    if (y2 < ymin) {
337
0
        lastPixel.x = INT_MIN;
338
0
        x2 += (x2 - x1)/(y2 - y1) * (ymin - y2);
339
0
        y2 = ymin;
340
60.2k
    } else if (y2 > ymax) {
341
0
        lastPixel.x = INT_MIN;
342
0
        x2 += (x2 - x1)/(y2 - y1) * (ymax - y2);
343
0
        y2 = ymax;
344
0
    }
345
346
60.2k
    return false;
347
348
0
  clipped:
349
0
    lastPixel.x = INT_MIN;
350
0
    return true;
351
60.2k
}
352
353
354
void QCosmeticStroker::drawLine(const QPointF &p1, const QPointF &p2)
355
0
{
356
0
    QPointF start = p1 * state->matrix;
357
0
    QPointF end = p2 * state->matrix;
358
359
0
    if (start == end) {
360
0
        drawPoints(&p1, 1);
361
0
        return;
362
0
    }
363
364
0
    patternOffset = state->lastPen.dashOffset()*64;
365
0
    lastPixel.x = INT_MIN;
366
0
    lastPixel.y = INT_MIN;
367
368
0
    stroke(this, start.x(), start.y(), end.x(), end.y(), drawCaps ? CapBegin|CapEnd : 0);
369
370
0
    blend(current_span, spans, &state->penData);
371
0
    current_span = 0;
372
0
}
373
374
void QCosmeticStroker::drawPoints(const QPoint *points, int num)
375
0
{
376
0
    const QPoint *end = points + num;
377
0
    while (points < end) {
378
0
        QPointF p = QPointF(*points) * state->matrix;
379
0
        drawPixel(this, std::floor(p.x()), std::floor(p.y()), 255);
380
0
        ++points;
381
0
    }
382
383
0
    blend(current_span, spans, &state->penData);
384
0
    current_span = 0;
385
0
}
386
387
void QCosmeticStroker::drawPoints(const QPointF *points, int num)
388
0
{
389
0
    const QPointF *end = points + num;
390
0
    while (points < end) {
391
0
        QPointF p = (*points) * state->matrix;
392
0
        drawPixel(this, std::floor(p.x()), std::floor(p.y()), 255);
393
0
        ++points;
394
0
    }
395
396
0
    blend(current_span, spans, &state->penData);
397
0
    current_span = 0;
398
0
}
399
400
void QCosmeticStroker::calculateLastPoint(qreal rx1, qreal ry1, qreal rx2, qreal ry2)
401
12.0k
{
402
    // this is basically the same code as used in the aliased stroke method,
403
    // but it only determines the direction and last point of a line
404
    //
405
    // This is being used to have proper dropout control for closed contours
406
    // by calculating the direction and last pixel of the last segment in the contour.
407
    // the info is then used to perform dropout control when drawing the first line segment
408
    // of the contour
409
12.0k
    lastPixel.x = INT_MIN;
410
12.0k
    lastPixel.y = INT_MIN;
411
412
12.0k
    if (clipLine(rx1, ry1, rx2, ry2))
413
0
        return;
414
415
12.0k
    int x1 = toF26Dot6(rx1);
416
12.0k
    int y1 = toF26Dot6(ry1);
417
12.0k
    int x2 = toF26Dot6(rx2);
418
12.0k
    int y2 = toF26Dot6(ry2);
419
420
12.0k
    int dx = qAbs(x2 - x1);
421
12.0k
    int dy = qAbs(y2 - y1);
422
423
12.0k
    if (dx < dy) {
424
        // vertical
425
12.0k
        bool swapped = false;
426
12.0k
        if (y1 > y2) {
427
12.0k
            swapped = true;
428
12.0k
            qSwap(y1, y2);
429
12.0k
            qSwap(x1, x2);
430
12.0k
        }
431
12.0k
        FDot16 xinc = FDot16FixedDiv(x2 - x1, y2 - y1);
432
12.0k
        FDot16 x = FDot16(x1) * (1<<10);
433
434
12.0k
        int y = (y1 + 32) >> 6;
435
12.0k
        int ys = (y2 + 32) >> 6;
436
437
12.0k
        int round = (xinc > 0) ? 32 : 0;
438
12.0k
        if (y != ys) {
439
12.0k
            x += ((y * (1<<6)) + round - y1) * xinc >> 6;
440
441
12.0k
            if (swapped) {
442
12.0k
                lastPixel.x = x >> 16;
443
12.0k
                lastPixel.y = y;
444
12.0k
                lastDir = QCosmeticStroker::BottomToTop;
445
12.0k
            } else {
446
0
                lastPixel.x = (x + (ys - y - 1)*xinc) >> 16;
447
0
                lastPixel.y = ys - 1;
448
0
                lastDir = QCosmeticStroker::TopToBottom;
449
0
            }
450
12.0k
            lastAxisAligned = qAbs(xinc) < (1 << 14);
451
12.0k
        }
452
12.0k
    } else {
453
        // horizontal
454
0
        if (!dx)
455
0
            return;
456
457
0
        bool swapped = false;
458
0
        if (x1 > x2) {
459
0
            swapped = true;
460
0
            qSwap(x1, x2);
461
0
            qSwap(y1, y2);
462
0
        }
463
0
        FDot16 yinc = FDot16FixedDiv(y2 - y1, x2 - x1);
464
0
        FDot16 y = FDot16(y1) * (1 << 10);
465
466
0
        int x = (x1 + 32) >> 6;
467
0
        int xs = (x2 + 32) >> 6;
468
469
0
        int round = (yinc > 0) ? 32 : 0;
470
0
        if (x != xs) {
471
0
            y += ((x * (1<<6)) + round - x1) * yinc >> 6;
472
473
0
            if (swapped) {
474
0
                lastPixel.x = x;
475
0
                lastPixel.y = y >> 16;
476
0
                lastDir = QCosmeticStroker::RightToLeft;
477
0
            } else {
478
0
                lastPixel.x = xs - 1;
479
0
                lastPixel.y = (y + (xs - x - 1)*yinc) >> 16;
480
0
                lastDir = QCosmeticStroker::LeftToRight;
481
0
            }
482
0
            lastAxisAligned = qAbs(yinc) < (1 << 14);
483
0
        }
484
0
    }
485
//    qDebug() << "   moveTo: setting last pixel to x/y dir" << lastPixel.x << lastPixel.y << lastDir;
486
12.0k
}
487
488
static inline const QPainterPath::ElementType *subPath(const QPainterPath::ElementType *t, const QPainterPath::ElementType *end,
489
                                                 const qreal *points, bool *closed)
490
0
{
491
0
    const QPainterPath::ElementType *start = t;
492
0
    ++t;
493
494
    // find out if the subpath is closed
495
0
    while (t < end) {
496
0
        if (*t == QPainterPath::MoveToElement)
497
0
            break;
498
0
        ++t;
499
0
    }
500
501
0
    int offset = t - start - 1;
502
//    qDebug() << "subpath" << offset << points[0] << points[1] << points[2*offset] << points[2*offset+1];
503
0
    *closed = (points[0] == points[2*offset] && points[1] == points[2*offset + 1]);
504
505
0
    return t;
506
0
}
507
508
void QCosmeticStroker::drawPath(const QVectorPath &path)
509
12.0k
{
510
//    qDebug() << ">>>> drawpath" << path.convertToPainterPath()
511
//             << "antialiasing:" << (bool)(state->renderHints & QPainter::Antialiasing) << " implicit close:" << path.hasImplicitClose();
512
12.0k
    if (path.isEmpty())
513
0
        return;
514
515
12.0k
    const qreal *points = path.points();
516
12.0k
    const QPainterPath::ElementType *type = path.elements();
517
518
12.0k
    if (type) {
519
0
        const QPainterPath::ElementType *end = type + path.elementCount();
520
521
0
        while (type < end) {
522
0
            Q_ASSERT(type == path.elements() || *type == QPainterPath::MoveToElement);
523
524
0
            QPointF p = QPointF(points[0], points[1]) * state->matrix;
525
0
            patternOffset = state->lastPen.dashOffset()*64;
526
0
            lastPixel.x = INT_MIN;
527
0
            lastPixel.y = INT_MIN;
528
529
0
            bool closed;
530
0
            const QPainterPath::ElementType *e = subPath(type, end, points, &closed);
531
0
            if (closed) {
532
0
                const qreal *p = points + 2*(e-type);
533
0
                QPointF p1 = QPointF(p[-4], p[-3]) * state->matrix;
534
0
                QPointF p2 = QPointF(p[-2], p[-1]) * state->matrix;
535
0
                calculateLastPoint(p1.x(), p1.y(), p2.x(), p2.y());
536
0
            }
537
0
            int caps = (!closed && drawCaps) ? CapBegin : NoCaps;
538
//            qDebug() << "closed =" << closed << capString(caps);
539
540
0
            points += 2;
541
0
            ++type;
542
543
0
            while (type < e) {
544
0
                QPointF p2 = QPointF(points[0], points[1]) * state->matrix;
545
0
                switch (*type) {
546
0
                case QPainterPath::MoveToElement:
547
0
                    Q_ASSERT(!"Logic error");
548
0
                    break;
549
550
0
                case QPainterPath::LineToElement:
551
0
                    if (!closed && drawCaps && type == e - 1)
552
0
                        caps |= CapEnd;
553
0
                    stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps);
554
0
                    p = p2;
555
0
                    points += 2;
556
0
                    ++type;
557
0
                    break;
558
559
0
                case QPainterPath::CurveToElement: {
560
0
                    if (!closed && drawCaps && type == e - 3)
561
0
                        caps |= CapEnd;
562
0
                    QPointF p3 = QPointF(points[2], points[3]) * state->matrix;
563
0
                    QPointF p4 = QPointF(points[4], points[5]) * state->matrix;
564
0
                    renderCubic(p, p2, p3, p4, caps);
565
0
                    p = p4;
566
0
                    type += 3;
567
0
                    points += 6;
568
0
                    break;
569
0
                }
570
0
                case QPainterPath::CurveToDataElement:
571
0
                    Q_ASSERT(!"QPainterPath::toSubpathPolygons(), bad element type");
572
0
                    break;
573
0
                }
574
0
                caps = NoCaps;
575
0
            }
576
0
        }
577
12.0k
    } else { // !type, simple polygon
578
12.0k
        QPointF p = QPointF(points[0], points[1]) * state->matrix;
579
12.0k
        QPointF movedTo = p;
580
12.0k
        patternOffset = state->lastPen.dashOffset()*64;
581
12.0k
        lastPixel.x = INT_MIN;
582
12.0k
        lastPixel.y = INT_MIN;
583
584
12.0k
        const qreal *begin = points;
585
12.0k
        const qreal *end = points + 2*path.elementCount();
586
        // handle closed path case
587
12.0k
        bool closed = path.hasImplicitClose() || (points[0] == end[-2] && points[1] == end[-1]);
588
12.0k
        int caps = (!closed && drawCaps) ? CapBegin : NoCaps;
589
12.0k
        if (closed) {
590
12.0k
            QPointF p2;
591
12.0k
            if (points[0] == end[-2] && points[1] == end[-1] && path.elementCount() > 2)
592
0
                p2 = QPointF(end[-4], end[-3]) * state->matrix;
593
12.0k
            else
594
12.0k
                p2 = QPointF(end[-2], end[-1]) * state->matrix;
595
12.0k
            calculateLastPoint(p2.x(), p2.y(), p.x(), p.y());
596
12.0k
        }
597
598
12.0k
        bool fastPenAliased = (state->flags.fast_pen && !state->flags.antialiased);
599
12.0k
        points += 2;
600
48.2k
        while (points < end) {
601
36.1k
            QPointF p2 = QPointF(points[0], points[1]) * state->matrix;
602
603
36.1k
            if (!closed && drawCaps && points == end - 2)
604
0
                caps |= CapEnd;
605
606
36.1k
            bool moveNextStart = stroke(this, p.x(), p.y(), p2.x(), p2.y(), caps);
607
608
            /* fix for gaps in polylines with fastpen and aliased in a sequence
609
               of points with small distances: if current point p2 has been dropped
610
               out, keep last non dropped point p.
611
612
               However, if the line was completely outside the devicerect, we
613
               still need to update p to avoid drawing the line after this one from
614
               a bad starting position.
615
            */
616
36.1k
            if (!fastPenAliased || moveNextStart || points == begin + 2 || points == end - 2)
617
36.1k
                p = p2;
618
36.1k
            points += 2;
619
36.1k
            caps = NoCaps;
620
36.1k
        }
621
12.0k
        if (path.hasImplicitClose())
622
12.0k
            stroke(this, p.x(), p.y(), movedTo.x(), movedTo.y(), NoCaps);
623
12.0k
    }
624
625
626
12.0k
    blend(current_span, spans, &state->penData);
627
12.0k
    current_span = 0;
628
12.0k
}
629
630
void QCosmeticStroker::renderCubic(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4, int caps)
631
0
{
632
//    qDebug() << ">>>> renderCubic" << p1 << p2 << p3 << p4 << capString(caps);
633
0
    const int maxSubDivisions = 6;
634
0
    PointF points[3*maxSubDivisions + 4];
635
636
0
    points[3].x = p1.x();
637
0
    points[3].y = p1.y();
638
0
    points[2].x = p2.x();
639
0
    points[2].y = p2.y();
640
0
    points[1].x = p3.x();
641
0
    points[1].y = p3.y();
642
0
    points[0].x = p4.x();
643
0
    points[0].y = p4.y();
644
645
0
    PointF *p = points;
646
0
    int level = maxSubDivisions;
647
648
0
    renderCubicSubdivision(p, level, caps);
649
0
}
650
651
static void splitCubic(QCosmeticStroker::PointF *points)
652
0
{
653
0
    const qreal half = .5;
654
0
    qreal  a, b, c, d;
655
656
0
    points[6].x = points[3].x;
657
0
    c = points[1].x;
658
0
    d = points[2].x;
659
0
    points[1].x = a = ( points[0].x + c ) * half;
660
0
    points[5].x = b = ( points[3].x + d ) * half;
661
0
    c = ( c + d ) * half;
662
0
    points[2].x = a = ( a + c ) * half;
663
0
    points[4].x = b = ( b + c ) * half;
664
0
    points[3].x = ( a + b ) * half;
665
666
0
    points[6].y = points[3].y;
667
0
    c = points[1].y;
668
0
    d = points[2].y;
669
0
    points[1].y = a = ( points[0].y + c ) * half;
670
0
    points[5].y = b = ( points[3].y + d ) * half;
671
0
    c = ( c + d ) * half;
672
0
    points[2].y = a = ( a + c ) * half;
673
0
    points[4].y = b = ( b + c ) * half;
674
0
    points[3].y = ( a + b ) * half;
675
0
}
676
677
void QCosmeticStroker::renderCubicSubdivision(QCosmeticStroker::PointF *points, int level, int caps)
678
0
{
679
0
    if (level) {
680
0
        qreal dx = points[3].x - points[0].x;
681
0
        qreal dy = points[3].y - points[0].y;
682
0
        qreal len = static_cast<qreal>(.25) * (qAbs(dx) + qAbs(dy));
683
684
0
        if (qAbs(dx * (points[0].y - points[2].y) - dy * (points[0].x - points[2].x)) >= len ||
685
0
            qAbs(dx * (points[0].y - points[1].y) - dy * (points[0].x - points[1].x)) >= len) {
686
0
            splitCubic(points);
687
688
0
            --level;
689
0
            renderCubicSubdivision(points + 3, level, caps & CapBegin);
690
0
            renderCubicSubdivision(points, level, caps & CapEnd);
691
0
            return;
692
0
        }
693
0
    }
694
695
0
    stroke(this, points[3].x, points[3].y, points[0].x, points[0].y, caps);
696
0
}
697
698
static inline int swapCaps(int caps)
699
24.1k
{
700
24.1k
    return ((caps & QCosmeticStroker::CapBegin) << 1) |
701
24.1k
           ((caps & QCosmeticStroker::CapEnd) >> 1);
702
24.1k
}
703
704
// adjust line by half a pixel
705
static inline void capAdjust(int caps, int &x1, int &x2, FDot16 &y, FDot16 yinc)
706
48.2k
{
707
48.2k
    if (caps & QCosmeticStroker::CapBegin) {
708
0
        x1 -= 32;
709
0
        y -= yinc >> 1;
710
0
    }
711
48.2k
    if (caps & QCosmeticStroker::CapEnd) {
712
0
        x2 += 32;
713
0
    }
714
48.2k
}
715
716
/*
717
  The hard part about this is dropout control and avoiding douple drawing of points when
718
  the drawing shifts from horizontal to vertical or back.
719
  */
720
template<DrawPixel drawPixel, class Dasher>
721
static bool drawLine(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps)
722
48.2k
{
723
48.2k
    bool didDraw = qAbs(rx2 - rx1) + qAbs(ry2 - ry1) >= 1.0;
724
725
48.2k
    if (stroker->clipLine(rx1, ry1, rx2, ry2))
726
0
        return true;
727
728
48.2k
    int x1 = toF26Dot6(rx1);
729
48.2k
    int y1 = toF26Dot6(ry1);
730
48.2k
    int x2 = toF26Dot6(rx2);
731
48.2k
    int y2 = toF26Dot6(ry2);
732
733
48.2k
    int dx = qAbs(x2 - x1);
734
48.2k
    int dy = qAbs(y2 - y1);
735
736
48.2k
    QCosmeticStroker::Point last = stroker->lastPixel;
737
738
//    qDebug() << "stroke" << x1/64. << y1/64. << x2/64. << y2/64.;
739
740
48.2k
    if (dx < dy) {
741
        // vertical
742
24.1k
        QCosmeticStroker::Direction dir = QCosmeticStroker::TopToBottom;
743
744
24.1k
        bool swapped = false;
745
24.1k
        if (y1 > y2) {
746
12.0k
            swapped = true;
747
12.0k
            qSwap(y1, y2);
748
12.0k
            qSwap(x1, x2);
749
12.0k
            caps = swapCaps(caps);
750
12.0k
            dir = QCosmeticStroker::BottomToTop;
751
12.0k
        }
752
24.1k
        FDot16 xinc = FDot16FixedDiv(x2 - x1, y2 - y1);
753
24.1k
        FDot16 x = FDot16(x1) * (1<<10);
754
755
24.1k
        if ((stroker->lastDir ^ QCosmeticStroker::VerticalMask) == dir)
756
0
            caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin;
757
758
24.1k
        capAdjust(caps, y1, y2, x, xinc);
759
760
24.1k
        int y = (y1 + 32) >> 6;
761
24.1k
        int ys = (y2 + 32) >> 6;
762
24.1k
        int round = (xinc > 0) ? 32 : 0;
763
764
        // If capAdjust made us round away from what calculateLastPoint gave us,
765
        // round back the other way so we start and end on the right point.
766
24.1k
        if ((caps & QCosmeticStroker::CapBegin) && stroker->lastPixel.y == y + 1)
767
0
           y++;
768
769
24.1k
        if (y != ys) {
770
24.1k
            x += ((y * (1<<6)) + round - y1) * xinc >> 6;
771
772
            // calculate first and last pixel and perform dropout control
773
24.1k
            QCosmeticStroker::Point first;
774
24.1k
            first.x = x >> 16;
775
24.1k
            first.y = y;
776
24.1k
            last.x = (x + (ys - y - 1)*xinc) >> 16;
777
24.1k
            last.y = ys - 1;
778
24.1k
            if (swapped)
779
12.0k
                qSwap(first, last);
780
781
24.1k
            bool axisAligned = qAbs(xinc) < (1 << 14);
782
24.1k
            if (stroker->lastPixel.x > INT_MIN) {
783
24.1k
                if (first.x == stroker->lastPixel.x &&
784
12.0k
                    first.y == stroker->lastPixel.y) {
785
                    // remove duplicated pixel
786
0
                    if (swapped) {
787
0
                        --ys;
788
0
                    } else {
789
0
                        ++y;
790
0
                        x += xinc;
791
0
                    }
792
24.1k
                } else if (stroker->lastDir != dir &&
793
24.1k
                           (((axisAligned && stroker->lastAxisAligned) &&
794
24.1k
                             stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) ||
795
24.1k
                            (qAbs(stroker->lastPixel.x - first.x) > 1 ||
796
24.1k
                             qAbs(stroker->lastPixel.y - first.y) > 1))) {
797
                    // have a missing pixel, insert it
798
0
                    if (swapped) {
799
0
                        ++ys;
800
0
                    } else {
801
0
                        --y;
802
0
                        x -= xinc;
803
0
                    }
804
24.1k
                } else if (stroker->lastDir == dir &&
805
0
                           ((qAbs(stroker->lastPixel.x - first.x) <= 1 &&
806
0
                             qAbs(stroker->lastPixel.y - first.y) > 1))) {
807
0
                    x += xinc >> 1;
808
0
                    if (swapped)
809
0
                        last.x = (x >> 16);
810
0
                    else
811
0
                        last.x = (x + (ys - y - 1)*xinc) >> 16;
812
0
                }
813
24.1k
            }
814
24.1k
            stroker->lastDir = dir;
815
24.1k
            stroker->lastAxisAligned = axisAligned;
816
817
24.1k
            Dasher dasher(stroker, swapped, y * (1<<6), ys * (1<<6));
818
819
24.1k
            do {
820
24.1k
                if (dasher.on())
821
24.1k
                    drawPixel(stroker, x >> 16, y, 255);
822
24.1k
                dasher.adjust();
823
24.1k
                x += xinc;
824
24.1k
            } while (++y < ys);
825
24.1k
            didDraw = true;
826
24.1k
        }
827
24.1k
    } else {
828
        // horizontal
829
24.1k
        if (!dx)
830
0
            return true;
831
832
24.1k
        QCosmeticStroker::Direction dir = QCosmeticStroker::LeftToRight;
833
834
24.1k
        bool swapped = false;
835
24.1k
        if (x1 > x2) {
836
12.0k
            swapped = true;
837
12.0k
            qSwap(x1, x2);
838
12.0k
            qSwap(y1, y2);
839
12.0k
            caps = swapCaps(caps);
840
12.0k
            dir = QCosmeticStroker::RightToLeft;
841
12.0k
        }
842
24.1k
        FDot16 yinc = FDot16FixedDiv(y2 - y1, x2 - x1);
843
24.1k
        FDot16 y = FDot16(y1) * (1<<10);
844
845
24.1k
        if ((stroker->lastDir ^ QCosmeticStroker::HorizontalMask) == dir)
846
0
            caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin;
847
848
24.1k
        capAdjust(caps, x1, x2, y, yinc);
849
850
24.1k
        int x = (x1 + 32) >> 6;
851
24.1k
        int xs = (x2 + 32) >> 6;
852
24.1k
        int round = (yinc > 0) ? 32 : 0;
853
854
        // If capAdjust made us round away from what calculateLastPoint gave us,
855
        // round back the other way so we start and end on the right point.
856
24.1k
        if ((caps & QCosmeticStroker::CapBegin) && stroker->lastPixel.x == x + 1)
857
0
            x++;
858
859
24.1k
        if (x != xs) {
860
24.1k
            y += ((x * (1<<6)) + round - x1) * yinc >> 6;
861
862
            // calculate first and last pixel to perform dropout control
863
24.1k
            QCosmeticStroker::Point first;
864
24.1k
            first.x = x;
865
24.1k
            first.y = y >> 16;
866
24.1k
            last.x = xs - 1;
867
24.1k
            last.y = (y + (xs - x - 1)*yinc) >> 16;
868
24.1k
            if (swapped)
869
12.0k
                qSwap(first, last);
870
871
24.1k
            bool axisAligned = qAbs(yinc) < (1 << 14);
872
24.1k
            if (stroker->lastPixel.x > INT_MIN) {
873
24.1k
                if (first.x == stroker->lastPixel.x && first.y == stroker->lastPixel.y) {
874
                    // remove duplicated pixel
875
12.0k
                    if (swapped) {
876
0
                        --xs;
877
12.0k
                    } else {
878
12.0k
                        ++x;
879
12.0k
                        y += yinc;
880
12.0k
                    }
881
12.0k
                } else if (stroker->lastDir != dir &&
882
12.0k
                           (((axisAligned && stroker->lastAxisAligned) &&
883
12.0k
                             stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) ||
884
0
                            (qAbs(stroker->lastPixel.x - first.x) > 1 ||
885
12.0k
                             qAbs(stroker->lastPixel.y - first.y) > 1))) {
886
                    // have a missing pixel, insert it
887
12.0k
                    if (swapped) {
888
12.0k
                        ++xs;
889
12.0k
                    } else {
890
0
                        --x;
891
0
                        y -= yinc;
892
0
                    }
893
12.0k
                } else if (stroker->lastDir == dir &&
894
0
                           ((qAbs(stroker->lastPixel.x - first.x) <= 1 &&
895
0
                             qAbs(stroker->lastPixel.y - first.y) > 1))) {
896
0
                    y += yinc >> 1;
897
0
                    if (swapped)
898
0
                        last.y = (y >> 16);
899
0
                    else
900
0
                        last.y = (y + (xs - x - 1)*yinc) >> 16;
901
0
                }
902
24.1k
            }
903
24.1k
            stroker->lastDir = dir;
904
24.1k
            stroker->lastAxisAligned = axisAligned;
905
906
24.1k
            Dasher dasher(stroker, swapped, x * (1<<6), xs * (1<<6));
907
908
36.1k
            do {
909
36.1k
                if (dasher.on())
910
36.1k
                    drawPixel(stroker, x, y >> 16, 255);
911
36.1k
                dasher.adjust();
912
36.1k
                y += yinc;
913
36.1k
            } while (++x < xs);
914
24.1k
            didDraw = true;
915
24.1k
        }
916
24.1k
    }
917
48.2k
    stroker->lastPixel = last;
918
48.2k
    return didDraw;
919
48.2k
}
Unexecuted instantiation: qcosmeticstroker.cpp:bool drawLine<&(drawPixel(QCosmeticStroker*, int, int, int)), (anonymous namespace)::NoDasher>(QCosmeticStroker*, double, double, double, double, int)
qcosmeticstroker.cpp:bool drawLine<&(drawPixelARGB32Opaque(QCosmeticStroker*, int, int, int)), (anonymous namespace)::NoDasher>(QCosmeticStroker*, double, double, double, double, int)
Line
Count
Source
722
48.2k
{
723
48.2k
    bool didDraw = qAbs(rx2 - rx1) + qAbs(ry2 - ry1) >= 1.0;
724
725
48.2k
    if (stroker->clipLine(rx1, ry1, rx2, ry2))
726
0
        return true;
727
728
48.2k
    int x1 = toF26Dot6(rx1);
729
48.2k
    int y1 = toF26Dot6(ry1);
730
48.2k
    int x2 = toF26Dot6(rx2);
731
48.2k
    int y2 = toF26Dot6(ry2);
732
733
48.2k
    int dx = qAbs(x2 - x1);
734
48.2k
    int dy = qAbs(y2 - y1);
735
736
48.2k
    QCosmeticStroker::Point last = stroker->lastPixel;
737
738
//    qDebug() << "stroke" << x1/64. << y1/64. << x2/64. << y2/64.;
739
740
48.2k
    if (dx < dy) {
741
        // vertical
742
24.1k
        QCosmeticStroker::Direction dir = QCosmeticStroker::TopToBottom;
743
744
24.1k
        bool swapped = false;
745
24.1k
        if (y1 > y2) {
746
12.0k
            swapped = true;
747
12.0k
            qSwap(y1, y2);
748
12.0k
            qSwap(x1, x2);
749
12.0k
            caps = swapCaps(caps);
750
12.0k
            dir = QCosmeticStroker::BottomToTop;
751
12.0k
        }
752
24.1k
        FDot16 xinc = FDot16FixedDiv(x2 - x1, y2 - y1);
753
24.1k
        FDot16 x = FDot16(x1) * (1<<10);
754
755
24.1k
        if ((stroker->lastDir ^ QCosmeticStroker::VerticalMask) == dir)
756
0
            caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin;
757
758
24.1k
        capAdjust(caps, y1, y2, x, xinc);
759
760
24.1k
        int y = (y1 + 32) >> 6;
761
24.1k
        int ys = (y2 + 32) >> 6;
762
24.1k
        int round = (xinc > 0) ? 32 : 0;
763
764
        // If capAdjust made us round away from what calculateLastPoint gave us,
765
        // round back the other way so we start and end on the right point.
766
24.1k
        if ((caps & QCosmeticStroker::CapBegin) && stroker->lastPixel.y == y + 1)
767
0
           y++;
768
769
24.1k
        if (y != ys) {
770
24.1k
            x += ((y * (1<<6)) + round - y1) * xinc >> 6;
771
772
            // calculate first and last pixel and perform dropout control
773
24.1k
            QCosmeticStroker::Point first;
774
24.1k
            first.x = x >> 16;
775
24.1k
            first.y = y;
776
24.1k
            last.x = (x + (ys - y - 1)*xinc) >> 16;
777
24.1k
            last.y = ys - 1;
778
24.1k
            if (swapped)
779
12.0k
                qSwap(first, last);
780
781
24.1k
            bool axisAligned = qAbs(xinc) < (1 << 14);
782
24.1k
            if (stroker->lastPixel.x > INT_MIN) {
783
24.1k
                if (first.x == stroker->lastPixel.x &&
784
12.0k
                    first.y == stroker->lastPixel.y) {
785
                    // remove duplicated pixel
786
0
                    if (swapped) {
787
0
                        --ys;
788
0
                    } else {
789
0
                        ++y;
790
0
                        x += xinc;
791
0
                    }
792
24.1k
                } else if (stroker->lastDir != dir &&
793
24.1k
                           (((axisAligned && stroker->lastAxisAligned) &&
794
24.1k
                             stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) ||
795
24.1k
                            (qAbs(stroker->lastPixel.x - first.x) > 1 ||
796
24.1k
                             qAbs(stroker->lastPixel.y - first.y) > 1))) {
797
                    // have a missing pixel, insert it
798
0
                    if (swapped) {
799
0
                        ++ys;
800
0
                    } else {
801
0
                        --y;
802
0
                        x -= xinc;
803
0
                    }
804
24.1k
                } else if (stroker->lastDir == dir &&
805
0
                           ((qAbs(stroker->lastPixel.x - first.x) <= 1 &&
806
0
                             qAbs(stroker->lastPixel.y - first.y) > 1))) {
807
0
                    x += xinc >> 1;
808
0
                    if (swapped)
809
0
                        last.x = (x >> 16);
810
0
                    else
811
0
                        last.x = (x + (ys - y - 1)*xinc) >> 16;
812
0
                }
813
24.1k
            }
814
24.1k
            stroker->lastDir = dir;
815
24.1k
            stroker->lastAxisAligned = axisAligned;
816
817
24.1k
            Dasher dasher(stroker, swapped, y * (1<<6), ys * (1<<6));
818
819
24.1k
            do {
820
24.1k
                if (dasher.on())
821
24.1k
                    drawPixel(stroker, x >> 16, y, 255);
822
24.1k
                dasher.adjust();
823
24.1k
                x += xinc;
824
24.1k
            } while (++y < ys);
825
24.1k
            didDraw = true;
826
24.1k
        }
827
24.1k
    } else {
828
        // horizontal
829
24.1k
        if (!dx)
830
0
            return true;
831
832
24.1k
        QCosmeticStroker::Direction dir = QCosmeticStroker::LeftToRight;
833
834
24.1k
        bool swapped = false;
835
24.1k
        if (x1 > x2) {
836
12.0k
            swapped = true;
837
12.0k
            qSwap(x1, x2);
838
12.0k
            qSwap(y1, y2);
839
12.0k
            caps = swapCaps(caps);
840
12.0k
            dir = QCosmeticStroker::RightToLeft;
841
12.0k
        }
842
24.1k
        FDot16 yinc = FDot16FixedDiv(y2 - y1, x2 - x1);
843
24.1k
        FDot16 y = FDot16(y1) * (1<<10);
844
845
24.1k
        if ((stroker->lastDir ^ QCosmeticStroker::HorizontalMask) == dir)
846
0
            caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin;
847
848
24.1k
        capAdjust(caps, x1, x2, y, yinc);
849
850
24.1k
        int x = (x1 + 32) >> 6;
851
24.1k
        int xs = (x2 + 32) >> 6;
852
24.1k
        int round = (yinc > 0) ? 32 : 0;
853
854
        // If capAdjust made us round away from what calculateLastPoint gave us,
855
        // round back the other way so we start and end on the right point.
856
24.1k
        if ((caps & QCosmeticStroker::CapBegin) && stroker->lastPixel.x == x + 1)
857
0
            x++;
858
859
24.1k
        if (x != xs) {
860
24.1k
            y += ((x * (1<<6)) + round - x1) * yinc >> 6;
861
862
            // calculate first and last pixel to perform dropout control
863
24.1k
            QCosmeticStroker::Point first;
864
24.1k
            first.x = x;
865
24.1k
            first.y = y >> 16;
866
24.1k
            last.x = xs - 1;
867
24.1k
            last.y = (y + (xs - x - 1)*yinc) >> 16;
868
24.1k
            if (swapped)
869
12.0k
                qSwap(first, last);
870
871
24.1k
            bool axisAligned = qAbs(yinc) < (1 << 14);
872
24.1k
            if (stroker->lastPixel.x > INT_MIN) {
873
24.1k
                if (first.x == stroker->lastPixel.x && first.y == stroker->lastPixel.y) {
874
                    // remove duplicated pixel
875
12.0k
                    if (swapped) {
876
0
                        --xs;
877
12.0k
                    } else {
878
12.0k
                        ++x;
879
12.0k
                        y += yinc;
880
12.0k
                    }
881
12.0k
                } else if (stroker->lastDir != dir &&
882
12.0k
                           (((axisAligned && stroker->lastAxisAligned) &&
883
12.0k
                             stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) ||
884
0
                            (qAbs(stroker->lastPixel.x - first.x) > 1 ||
885
12.0k
                             qAbs(stroker->lastPixel.y - first.y) > 1))) {
886
                    // have a missing pixel, insert it
887
12.0k
                    if (swapped) {
888
12.0k
                        ++xs;
889
12.0k
                    } else {
890
0
                        --x;
891
0
                        y -= yinc;
892
0
                    }
893
12.0k
                } else if (stroker->lastDir == dir &&
894
0
                           ((qAbs(stroker->lastPixel.x - first.x) <= 1 &&
895
0
                             qAbs(stroker->lastPixel.y - first.y) > 1))) {
896
0
                    y += yinc >> 1;
897
0
                    if (swapped)
898
0
                        last.y = (y >> 16);
899
0
                    else
900
0
                        last.y = (y + (xs - x - 1)*yinc) >> 16;
901
0
                }
902
24.1k
            }
903
24.1k
            stroker->lastDir = dir;
904
24.1k
            stroker->lastAxisAligned = axisAligned;
905
906
24.1k
            Dasher dasher(stroker, swapped, x * (1<<6), xs * (1<<6));
907
908
36.1k
            do {
909
36.1k
                if (dasher.on())
910
36.1k
                    drawPixel(stroker, x, y >> 16, 255);
911
36.1k
                dasher.adjust();
912
36.1k
                y += yinc;
913
36.1k
            } while (++x < xs);
914
24.1k
            didDraw = true;
915
24.1k
        }
916
24.1k
    }
917
48.2k
    stroker->lastPixel = last;
918
48.2k
    return didDraw;
919
48.2k
}
Unexecuted instantiation: qcosmeticstroker.cpp:bool drawLine<&(drawPixel(QCosmeticStroker*, int, int, int)), (anonymous namespace)::Dasher>(QCosmeticStroker*, double, double, double, double, int)
Unexecuted instantiation: qcosmeticstroker.cpp:bool drawLine<&(drawPixelARGB32Opaque(QCosmeticStroker*, int, int, int)), (anonymous namespace)::Dasher>(QCosmeticStroker*, double, double, double, double, int)
920
921
922
template<DrawPixel drawPixel, class Dasher>
923
static bool drawLineAA(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps)
924
0
{
925
0
    if (stroker->clipLine(rx1, ry1, rx2, ry2))
926
0
        return true;
927
928
0
    int x1 = toF26Dot6(rx1);
929
0
    int y1 = toF26Dot6(ry1);
930
0
    int x2 = toF26Dot6(rx2);
931
0
    int y2 = toF26Dot6(ry2);
932
933
0
    int dx = x2 - x1;
934
0
    int dy = y2 - y1;
935
936
0
    if (qAbs(dx) < qAbs(dy)) {
937
        // vertical
938
939
0
        FDot16 xinc = FDot16FixedDiv(dx, dy);
940
941
0
        bool swapped = false;
942
0
        if (y1 > y2) {
943
0
            qSwap(y1, y2);
944
0
            qSwap(x1, x2);
945
0
            swapped = true;
946
0
            caps = swapCaps(caps);
947
0
        }
948
949
0
        FDot16 x = FDot16(x1 - 32) * (1<<10);
950
0
        x -= ( ((y1 & 63) - 32)  * xinc ) >> 6;
951
952
0
        capAdjust(caps, y1, y2, x, xinc);
953
954
0
        Dasher dasher(stroker, swapped, y1, y2);
955
956
0
        int y = y1 >> 6;
957
0
        int ys = y2 >> 6;
958
959
0
        int alphaStart, alphaEnd;
960
0
        if (y == ys) {
961
0
            alphaStart = y2 - y1;
962
0
            Q_ASSERT(alphaStart >= 0 && alphaStart < 64);
963
0
            alphaEnd = 0;
964
0
        } else {
965
0
            alphaStart = 64 - (y1 & 63);
966
0
            alphaEnd = (y2 & 63);
967
0
        }
968
//        qDebug() << "vertical" << x1/64. << y1/64. << x2/64. << y2/64.;
969
//        qDebug() << "          x=" << x << "dx=" << dx << "xi=" << (x>>16) << "xsi=" << ((x+(ys-y)*dx)>>16) << "y=" << y << "ys=" << ys;
970
971
        // draw first pixel
972
0
        if (dasher.on()) {
973
0
            uint alpha = static_cast<quint8>(x >> 8);
974
0
            drawPixel(stroker, x>>16, y, (255-alpha) * alphaStart >> 6);
975
0
            drawPixel(stroker, (x>>16) + 1, y, alpha * alphaStart >> 6);
976
0
        }
977
0
        dasher.adjust();
978
0
        x += xinc;
979
0
        ++y;
980
0
        if (y < ys) {
981
0
            do {
982
0
                if (dasher.on()) {
983
0
                    uint alpha = static_cast<quint8>(x >> 8);
984
0
                    drawPixel(stroker, x>>16, y, (255-alpha));
985
0
                    drawPixel(stroker, (x>>16) + 1, y, alpha);
986
0
                }
987
0
                dasher.adjust();
988
0
                x += xinc;
989
0
            } while (++y < ys);
990
0
        }
991
        // draw last pixel
992
0
        if (alphaEnd && dasher.on()) {
993
0
            uint alpha = static_cast<quint8>(x >> 8);
994
0
            drawPixel(stroker, x>>16, y, (255-alpha) * alphaEnd >> 6);
995
0
            drawPixel(stroker, (x>>16) + 1, y, alpha * alphaEnd >> 6);
996
0
        }
997
0
    } else {
998
        // horizontal
999
0
        if (!dx)
1000
0
            return true;
1001
1002
0
        FDot16 yinc = FDot16FixedDiv(dy, dx);
1003
1004
0
        bool swapped = false;
1005
0
        if (x1 > x2) {
1006
0
            qSwap(x1, x2);
1007
0
            qSwap(y1, y2);
1008
0
            swapped = true;
1009
0
            caps = swapCaps(caps);
1010
0
        }
1011
1012
0
        FDot16 y = FDot16(y1 - 32) * (1<<10);
1013
0
        y -= ( ((x1 & 63) - 32)  * yinc ) >> 6;
1014
1015
0
        capAdjust(caps, x1, x2, y, yinc);
1016
1017
0
        Dasher dasher(stroker, swapped, x1, x2);
1018
1019
0
        int x = x1 >> 6;
1020
0
        int xs = x2 >> 6;
1021
1022
//        qDebug() << "horizontal" << x1/64. << y1/64. << x2/64. << y2/64.;
1023
//        qDebug() << "          y=" << y << "dy=" << dy << "x=" << x << "xs=" << xs << "yi=" << (y>>16) << "ysi=" << ((y+(xs-x)*dy)>>16);
1024
0
        int alphaStart, alphaEnd;
1025
0
        if (x == xs) {
1026
0
            alphaStart = x2 - x1;
1027
0
            Q_ASSERT(alphaStart >= 0 && alphaStart < 64);
1028
0
            alphaEnd = 0;
1029
0
        } else {
1030
0
            alphaStart = 64 - (x1 & 63);
1031
0
            alphaEnd = (x2 & 63);
1032
0
        }
1033
1034
        // draw first pixel
1035
0
        if (dasher.on()) {
1036
0
            uint alpha = static_cast<quint8>(y >> 8);
1037
0
            drawPixel(stroker, x, y>>16, (255-alpha) * alphaStart >> 6);
1038
0
            drawPixel(stroker, x, (y>>16) + 1, alpha * alphaStart >> 6);
1039
0
        }
1040
0
        dasher.adjust();
1041
0
        y += yinc;
1042
0
        ++x;
1043
        // draw line
1044
0
        if (x < xs) {
1045
0
            do {
1046
0
                if (dasher.on()) {
1047
0
                    uint alpha = static_cast<quint8>(y >> 8);
1048
0
                    drawPixel(stroker, x, y>>16, (255-alpha));
1049
0
                    drawPixel(stroker, x, (y>>16) + 1, alpha);
1050
0
                }
1051
0
                dasher.adjust();
1052
0
                y += yinc;
1053
0
            } while (++x < xs);
1054
0
        }
1055
        // draw last pixel
1056
0
        if (alphaEnd && dasher.on()) {
1057
0
            uint alpha = static_cast<quint8>(y >> 8);
1058
0
            drawPixel(stroker, x, y>>16, (255-alpha) * alphaEnd >> 6);
1059
0
            drawPixel(stroker, x, (y>>16) + 1, alpha * alphaEnd >> 6);
1060
0
        }
1061
0
    }
1062
0
    return true;
1063
0
}
Unexecuted instantiation: qcosmeticstroker.cpp:bool drawLineAA<&(drawPixel(QCosmeticStroker*, int, int, int)), (anonymous namespace)::NoDasher>(QCosmeticStroker*, double, double, double, double, int)
Unexecuted instantiation: qcosmeticstroker.cpp:bool drawLineAA<&(drawPixelARGB32(QCosmeticStroker*, int, int, int)), (anonymous namespace)::NoDasher>(QCosmeticStroker*, double, double, double, double, int)
Unexecuted instantiation: qcosmeticstroker.cpp:bool drawLineAA<&(drawPixel(QCosmeticStroker*, int, int, int)), (anonymous namespace)::Dasher>(QCosmeticStroker*, double, double, double, double, int)
Unexecuted instantiation: qcosmeticstroker.cpp:bool drawLineAA<&(drawPixelARGB32(QCosmeticStroker*, int, int, int)), (anonymous namespace)::Dasher>(QCosmeticStroker*, double, double, double, double, int)
1064
1065
QT_END_NAMESPACE