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