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