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