Coverage Report

Created: 2026-04-29 07:00

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