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