Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qpathsimplifier.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
4#include "qpathsimplifier_p.h"
5
6#include <QtCore/qvarlengtharray.h>
7#include <QtCore/qglobal.h>
8#include <QtCore/qpoint.h>
9#include <QtCore/qalgorithms.h>
10
11#if QT_CONFIG(opengl)
12# include <private/qopengl_p.h>
13#endif
14#include <private/qrbtree_p.h>
15
17
18#define Q_FIXED_POINT_SCALE 256
19#define Q_TRIANGULATE_END_OF_POLYGON quint32(-1)
20
21
22
23//============================================================================//
24// QPoint //
25//============================================================================//
26
27inline bool operator < (const QPoint &a, const QPoint &b)
28{
29 return a.y() < b.y() || (a.y() == b.y() && a.x() < b.x());
30}
31
32inline bool operator > (const QPoint &a, const QPoint &b)
33{
34 return b < a;
35}
36
37inline bool operator <= (const QPoint &a, const QPoint &b)
38{
39 return !(a > b);
40}
41
42inline bool operator >= (const QPoint &a, const QPoint &b)
43{
44 return !(a < b);
45}
46
47namespace {
48
49inline int cross(const QPoint &u, const QPoint &v)
50{
51 return u.x() * v.y() - u.y() * v.x();
52}
53
54inline int dot(const QPoint &u, const QPoint &v)
55{
56 return u.x() * v.x() + u.y() * v.y();
57}
58
59//============================================================================//
60// Fraction //
61//============================================================================//
62
63// Fraction must be in the range [0, 1)
64struct Fraction
65{
66 bool isValid() const { return denominator != 0; }
67
68 // numerator and denominator must not have common denominators.
69 unsigned int numerator, denominator;
70};
71
72inline unsigned int gcd(unsigned int x, unsigned int y)
73{
74 while (y != 0) {
75 unsigned int z = y;
76 y = x % y;
77 x = z;
78 }
79 return x;
80}
81
82// Fraction must be in the range [0, 1)
83// Assume input is valid.
84Fraction fraction(unsigned int n, unsigned int d) {
85 Fraction result;
86 if (n == 0) {
87 result.numerator = 0;
88 result.denominator = 1;
89 } else {
90 unsigned int g = gcd(n, d);
91 result.numerator = n / g;
92 result.denominator = d / g;
93 }
94 return result;
95}
96
97//============================================================================//
98// Rational //
99//============================================================================//
100
101struct Rational
102{
103 int integer;
104 Fraction fraction;
105};
106
107//============================================================================//
108// IntersectionPoint //
109//============================================================================//
110
111struct IntersectionPoint
112{
113 bool isValid() const { return x.fraction.isValid() && y.fraction.isValid(); }
114 QPoint round() const;
115 bool isAccurate() const { return x.fraction.numerator == 0 && y.fraction.numerator == 0; }
116
117 Rational x; // 8:8 signed, 32/32
118 Rational y; // 8:8 signed, 32/32
119};
120
121QPoint IntersectionPoint::round() const
122{
123 QPoint result(x.integer, y.integer);
124 if (2 * x.fraction.numerator >= x.fraction.denominator)
125 ++result.rx();
126 if (2 * y.fraction.numerator >= y.fraction.denominator)
127 ++result.ry();
128 return result;
129}
130
131// Return positive value if 'p' is to the right of the line 'v1'->'v2', negative if left of the
132// line and zero if exactly on the line.
133// The returned value is the z-component of the qCross product between 'v2-v1' and 'p-v1',
134// which is twice the signed area of the triangle 'p'->'v1'->'v2' (positive for CW order).
135inline int pointDistanceFromLine(const QPoint &p, const QPoint &v1, const QPoint &v2)
136{
137 return cross(v2 - v1, p - v1);
138}
139
140IntersectionPoint intersectionPoint(const QPoint &u1, const QPoint &u2,
141 const QPoint &v1, const QPoint &v2)
142{
143 IntersectionPoint result = {{0, {0, 0}}, {0, {0, 0}}};
144
145 QPoint u = u2 - u1;
146 QPoint v = v2 - v1;
147 int d1 = cross(u, v1 - u1);
148 int d2 = cross(u, v2 - u1);
149 int det = d2 - d1;
150 int d3 = cross(v, u1 - v1);
151 int d4 = d3 - det; //qCross(v, u2 - v1);
152
153 // Check that the math is correct.
154 Q_ASSERT(d4 == cross(v, u2 - v1));
155
156 // The intersection point can be expressed as:
157 // v1 - v * d1/det
158 // v2 - v * d2/det
159 // u1 + u * d3/det
160 // u2 + u * d4/det
161
162 // I'm only interested in lines that are crossing, so ignore parallel lines even if they overlap.
163 if (det == 0)
164 return result;
165
166 if (det < 0) {
167 det = -det;
168 d1 = -d1;
169 d2 = -d2;
170 d3 = -d3;
171 d4 = -d4;
172 }
173
174 // I'm only interested in lines intersecting at their interior, not at their end points.
175 // The lines intersect at their interior if and only if 'd1 < 0', 'd2 > 0', 'd3 < 0' and 'd4 > 0'.
176 if (d1 >= 0 || d2 <= 0 || d3 <= 0 || d4 >= 0)
177 return result;
178
179 // Calculate the intersection point as follows:
180 // v1 - v * d1/det | v1 <= v2 (component-wise)
181 // v2 - v * d2/det | v2 < v1 (component-wise)
182
183 // Assuming 16 bits per vector component.
184 if (v.x() >= 0) {
185 result.x.integer = v1.x() + int(qint64(-v.x()) * d1 / det);
186 result.x.fraction = fraction((unsigned int)(qint64(-v.x()) * d1 % det), (unsigned int)det);
187 } else {
188 result.x.integer = v2.x() + int(qint64(-v.x()) * d2 / det);
189 result.x.fraction = fraction((unsigned int)(qint64(-v.x()) * d2 % det), (unsigned int)det);
190 }
191
192 if (v.y() >= 0) {
193 result.y.integer = v1.y() + int(qint64(-v.y()) * d1 / det);
194 result.y.fraction = fraction((unsigned int)(qint64(-v.y()) * d1 % det), (unsigned int)det);
195 } else {
196 result.y.integer = v2.y() + int(qint64(-v.y()) * d2 / det);
197 result.y.fraction = fraction((unsigned int)(qint64(-v.y()) * d2 % det), (unsigned int)det);
198 }
199
200 Q_ASSERT(result.x.fraction.isValid());
201 Q_ASSERT(result.y.fraction.isValid());
202 return result;
203}
204
205//============================================================================//
206// PathSimplifier //
207//============================================================================//
208
209class PathSimplifier
210{
211public:
212 PathSimplifier(const QVectorPath &path, QDataBuffer<QPoint> &vertices,
213 QDataBuffer<quint32> &indices, const QTransform &matrix);
214
215private:
216 struct Element;
217
218 class BoundingVolumeHierarchy
219 {
220 public:
221 struct Node
222 {
223 enum Type
224 {
225 Leaf,
226 Split
227 };
228 Type type;
229 QPoint minimum;
230 QPoint maximum;
231 union {
232 Element *element; // type == Leaf
233 Node *left; // type == Split
234 };
235 Node *right;
236 };
237
238 BoundingVolumeHierarchy();
239 ~BoundingVolumeHierarchy();
240 void allocate(int nodeCount);
241 void free();
242 Node *newNode();
243
244 Node *root;
245 private:
246 void freeNode(Node *n);
247
248 Node *nodeBlock;
249 int blockSize;
250 int firstFree;
251 };
252
253 struct Element
254 {
255 enum Degree
256 {
257 Line = 1,
258 Quadratic = 2,
259 Cubic = 3
260 };
261
262 quint32 &upperIndex() { return indices[pointingUp ? degree : 0]; }
263 quint32 &lowerIndex() { return indices[pointingUp ? 0 : degree]; }
264 quint32 upperIndex() const { return indices[pointingUp ? degree : 0]; }
265 quint32 lowerIndex() const { return indices[pointingUp ? 0 : degree]; }
266 void flip();
267
268 QPoint middle;
269 quint32 indices[4]; // index to points
270 Element *next, *previous; // used in connectElements()
271 int winding; // used in connectElements()
272 union {
273 QRBTree<Element *>::Node *edgeNode; // used in connectElements()
274 BoundingVolumeHierarchy::Node *bvhNode;
275 };
276 Degree degree : 8;
277 uint processed : 1; // initially false, true when the element has been checked for intersections.
278 uint pointingUp : 1; // used in connectElements()
279 uint originallyPointingUp : 1; // used in connectElements()
280 };
281
282 class ElementAllocator
283 {
284 public:
285 ElementAllocator();
286 ~ElementAllocator();
287 void allocate(int count);
288 Element *newElement();
289 private:
290 struct ElementBlock
291 {
292 ElementBlock *next;
293 int blockSize;
294 int firstFree;
295 Element elements[1];
296 } *blocks;
297 };
298
299 struct Event
300 {
301 enum Type { Upper, Lower };
302 bool operator < (const Event &other) const;
303
304 QPoint point;
305 Type type;
306 Element *element;
307 };
308 friend class QTypeInfo<Event>;
309
310 typedef QRBTree<Element *>::Node RBNode;
311 typedef BoundingVolumeHierarchy::Node BVHNode;
312
313 void initElements(const QVectorPath &path, const QTransform &matrix);
314 void removeIntersections();
315 bool connectElements();
316 void fillIndices();
317 BVHNode *buildTree(Element **elements, int elementCount);
318 bool intersectNodes(QDataBuffer<Element *> &elements, BVHNode *elementNode, BVHNode *treeNode);
319 bool equalElements(const Element *e1, const Element *e2);
320 bool splitLineAt(QDataBuffer<Element *> &elements, BVHNode *node, quint32 pointIndex, bool processAgain);
321 void appendSeparatingAxes(QVarLengthArray<QPoint, 12> &axes, Element *element);
322 QPair<int, int> calculateSeparatingAxisRange(const QPoint &axis, Element *element);
323 void splitCurve(QDataBuffer<Element *> &elements, BVHNode *node);
324 bool setElementToQuadratic(Element *element, quint32 pointIndex1, const QPoint &ctrl, quint32 pointIndex2);
325 bool setElementToCubic(Element *element, quint32 pointIndex1, const QPoint &ctrl1, const QPoint &ctrl2, quint32 pointIndex2);
326 void setElementToCubicAndSimplify(Element *element, quint32 pointIndex1, const QPoint &ctrl1, const QPoint &ctrl2, quint32 pointIndex2);
327 RBNode *findElementLeftOf(const Element *element, const QPair<RBNode *, RBNode *> &bounds);
328 bool elementIsLeftOf(const Element *left, const Element *right);
329 QPair<RBNode *, RBNode *> outerBounds(const QPoint &point);
330 static bool flattenQuadratic(const QPoint &u, const QPoint &v, const QPoint &w);
331 static bool flattenCubic(const QPoint &u, const QPoint &v, const QPoint &w, const QPoint &q);
332 static bool splitQuadratic(const QPoint &u, const QPoint &v, const QPoint &w, QPoint *result);
333 static bool splitCubic(const QPoint &u, const QPoint &v, const QPoint &w, const QPoint &q, QPoint *result);
334 void subDivQuadratic(const QPoint &u, const QPoint &v, const QPoint &w);
335 void subDivCubic(const QPoint &u, const QPoint &v, const QPoint &w, const QPoint &q);
336 static void sortEvents(Event *events, int count);
337
338 ElementAllocator m_elementAllocator;
339 QDataBuffer<Element *> m_elements;
340 QDataBuffer<QPoint> *m_points;
341 BoundingVolumeHierarchy m_bvh;
342 QDataBuffer<quint32> *m_indices;
343 QRBTree<Element *> m_elementList;
344 uint m_hints;
345};
346
347} // unnamed namespace
348
349Q_DECLARE_TYPEINFO(PathSimplifier::Event, Q_PRIMITIVE_TYPE);
350
351inline PathSimplifier::BoundingVolumeHierarchy::BoundingVolumeHierarchy()
352 : root(nullptr)
353 , nodeBlock(nullptr)
354 , blockSize(0)
355 , firstFree(0)
356{
357}
358
359inline PathSimplifier::BoundingVolumeHierarchy::~BoundingVolumeHierarchy()
360{
361 free();
362}
363
364inline void PathSimplifier::BoundingVolumeHierarchy::allocate(int nodeCount)
365{
366 Q_ASSERT(nodeBlock == nullptr);
367 Q_ASSERT(firstFree == 0);
368 nodeBlock = new Node[blockSize = nodeCount];
369}
370
371inline void PathSimplifier::BoundingVolumeHierarchy::free()
372{
373 freeNode(root);
374 delete[] nodeBlock;
375 nodeBlock = nullptr;
376 firstFree = blockSize = 0;
377 root = nullptr;
378}
379
380inline PathSimplifier::BVHNode *PathSimplifier::BoundingVolumeHierarchy::newNode()
381{
382 if (firstFree < blockSize)
383 return &nodeBlock[firstFree++];
384 return new Node;
385}
386
387inline void PathSimplifier::BoundingVolumeHierarchy::freeNode(Node *n)
388{
389 if (!n)
390 return;
391 Q_ASSERT(n->type == Node::Split || n->type == Node::Leaf);
392 if (n->type == Node::Split) {
393 freeNode(n->left);
394 freeNode(n->right);
395 }
396 if (!(n >= nodeBlock && n < nodeBlock + blockSize))
397 delete n;
398}
399
400inline PathSimplifier::ElementAllocator::ElementAllocator()
401 : blocks(nullptr)
402{
403}
404
405inline PathSimplifier::ElementAllocator::~ElementAllocator()
406{
407 while (blocks) {
408 ElementBlock *block = blocks;
409 blocks = blocks->next;
410 free(block);
411 }
412}
413
414inline void PathSimplifier::ElementAllocator::allocate(int count)
415{
416 Q_ASSERT(blocks == nullptr);
417 Q_ASSERT(count > 0);
418 blocks = (ElementBlock *)malloc(sizeof(ElementBlock) + (count - 1) * sizeof(Element));
419 blocks->blockSize = count;
420 blocks->next = nullptr;
421 blocks->firstFree = 0;
422}
423
424inline PathSimplifier::Element *PathSimplifier::ElementAllocator::newElement()
425{
426 Q_ASSERT(blocks);
427 if (blocks->firstFree < blocks->blockSize)
428 return &blocks->elements[blocks->firstFree++];
429 ElementBlock *oldBlock = blocks;
430 blocks = (ElementBlock *)malloc(sizeof(ElementBlock) + (oldBlock->blockSize - 1) * sizeof(Element));
431 blocks->blockSize = oldBlock->blockSize;
432 blocks->next = oldBlock;
433 blocks->firstFree = 0;
434 return &blocks->elements[blocks->firstFree++];
435}
436
437
438inline bool PathSimplifier::Event::operator < (const Event &other) const
439{
440 if (point == other.point)
441 return type < other.type;
442 return other.point < point;
443}
444
445inline void PathSimplifier::Element::flip()
446{
447 for (int i = 0; i < (degree + 1) >> 1; ++i) {
448 Q_ASSERT(degree >= Line && degree <= Cubic);
449 Q_ASSERT(i >= 0 && i < degree);
450 qSwap(indices[i], indices[degree - i]);
451 }
452 pointingUp = !pointingUp;
453 Q_ASSERT(next == nullptr && previous == nullptr);
454}
455
456PathSimplifier::PathSimplifier(const QVectorPath &path, QDataBuffer<QPoint> &vertices,
457 QDataBuffer<quint32> &indices, const QTransform &matrix)
458 : m_elements(0)
459 , m_points(&vertices)
460 , m_indices(&indices)
461{
462 m_points->reset();
463 m_indices->reset();
464 bool ok = true;
465 initElements(path, matrix);
466 if (!m_elements.isEmpty()) {
467 removeIntersections();
468 ok = connectElements();
469 if (ok)
470 fillIndices();
471 }
472 if (!ok) {
473 m_points->reset();
474 m_indices->reset();
475 }
476}
477
478void PathSimplifier::initElements(const QVectorPath &path, const QTransform &matrix)
479{
480 m_hints = path.hints();
481 int pathElementCount = path.elementCount();
482 if (pathElementCount == 0)
483 return;
484 m_elements.reserve(2 * pathElementCount);
485 m_elementAllocator.allocate(2 * pathElementCount);
486 m_points->reserve(2 * pathElementCount);
487 const QPainterPath::ElementType *e = path.elements();
488 const qreal *p = path.points();
489 if (e) {
490 qreal x, y;
491 quint32 moveToIndex = 0;
492 quint32 previousIndex = 0;
493 for (int i = 0; i < pathElementCount; ++i, ++e, p += 2) {
494 switch (*e) {
496 {
497 if (!m_points->isEmpty()) {
498 const QPoint &from = m_points->at(previousIndex);
499 const QPoint &to = m_points->at(moveToIndex);
500 if (from != to) {
501 Element *element = m_elementAllocator.newElement();
502 element->degree = Element::Line;
503 element->indices[0] = previousIndex;
504 element->indices[1] = moveToIndex;
505 element->middle.rx() = (from.x() + to.x()) >> 1;
506 element->middle.ry() = (from.y() + to.y()) >> 1;
507 m_elements.add(element);
508 }
509 }
510 previousIndex = moveToIndex = m_points->size();
511 matrix.map(p[0], p[1], &x, &y);
513 m_points->add(to);
514 }
515 break;
517 Q_ASSERT(!m_points->isEmpty());
518 {
519 matrix.map(p[0], p[1], &x, &y);
521 const QPoint &from = m_points->last();
522 if (to != from) {
523 Element *element = m_elementAllocator.newElement();
524 element->degree = Element::Line;
525 element->indices[0] = previousIndex;
526 element->indices[1] = quint32(m_points->size());
527 element->middle.rx() = (from.x() + to.x()) >> 1;
528 element->middle.ry() = (from.y() + to.y()) >> 1;
529 m_elements.add(element);
530 previousIndex = m_points->size();
531 m_points->add(to);
532 }
533 }
534 break;
536 Q_ASSERT(i + 2 < pathElementCount);
537 Q_ASSERT(!m_points->isEmpty());
540 {
541 quint32 startPointIndex = previousIndex;
542 matrix.map(p[4], p[5], &x, &y);
544 previousIndex = m_points->size();
545 m_points->add(end);
546
547 // See if this cubic bezier is really quadratic.
548 qreal x1 = p[-2] + qreal(1.5) * (p[0] - p[-2]);
549 qreal y1 = p[-1] + qreal(1.5) * (p[1] - p[-1]);
550 qreal x2 = p[4] + qreal(1.5) * (p[2] - p[4]);
551 qreal y2 = p[5] + qreal(1.5) * (p[3] - p[5]);
552
553 Element *element = m_elementAllocator.newElement();
554 if (qAbs(x1 - x2) < qreal(1e-3) && qAbs(y1 - y2) < qreal(1e-3)) {
555 // The bezier curve is quadratic.
556 matrix.map(x1, y1, &x, &y);
559 setElementToQuadratic(element, startPointIndex, ctrl, previousIndex);
560 } else {
561 // The bezier curve is cubic.
562 matrix.map(p[0], p[1], &x, &y);
565 matrix.map(p[2], p[3], &x, &y);
568 setElementToCubicAndSimplify(element, startPointIndex, ctrl1, ctrl2,
569 previousIndex);
570 }
571 m_elements.add(element);
572 }
573 i += 2;
574 e += 2;
575 p += 4;
576
577 break;
578 default:
579 Q_ASSERT_X(0, "QSGPathSimplifier::initialize", "Unexpected element type.");
580 break;
581 }
582 }
583 if (!m_points->isEmpty()) {
584 const QPoint &from = m_points->at(previousIndex);
585 const QPoint &to = m_points->at(moveToIndex);
586 if (from != to) {
587 Element *element = m_elementAllocator.newElement();
588 element->degree = Element::Line;
589 element->indices[0] = previousIndex;
590 element->indices[1] = moveToIndex;
591 element->middle.rx() = (from.x() + to.x()) >> 1;
592 element->middle.ry() = (from.y() + to.y()) >> 1;
593 m_elements.add(element);
594 }
595 }
596 } else {
597 qreal x, y;
598
599 for (int i = 0; i < pathElementCount; ++i, p += 2) {
600 matrix.map(p[0], p[1], &x, &y);
602 if (to != m_points->last())
603 m_points->add(to);
604 }
605
606 while (!m_points->isEmpty() && m_points->last() == m_points->first())
607 m_points->pop_back();
608
609 if (m_points->isEmpty())
610 return;
611
612 quint32 prev = quint32(m_points->size() - 1);
613 for (int i = 0; i < m_points->size(); ++i) {
614 QPoint &to = m_points->at(i);
615 QPoint &from = m_points->at(prev);
616 Element *element = m_elementAllocator.newElement();
617 element->degree = Element::Line;
618 element->indices[0] = prev;
619 element->indices[1] = quint32(i);
620 element->middle.rx() = (from.x() + to.x()) >> 1;
621 element->middle.ry() = (from.y() + to.y()) >> 1;
622 m_elements.add(element);
623 prev = i;
624 }
625 }
626
627 for (int i = 0; i < m_elements.size(); ++i)
628 m_elements.at(i)->processed = false;
629}
630
631void PathSimplifier::removeIntersections()
632{
633 Q_ASSERT(!m_elements.isEmpty());
634 QDataBuffer<Element *> elements(m_elements.size());
635 for (int i = 0; i < m_elements.size(); ++i)
636 elements.add(m_elements.at(i));
637 m_bvh.allocate(2 * m_elements.size());
638 m_bvh.root = buildTree(elements.data(), elements.size());
639
640 elements.reset();
641 for (int i = 0; i < m_elements.size(); ++i)
642 elements.add(m_elements.at(i));
643
644 while (!elements.isEmpty()) {
645 Element *element = elements.last();
646 elements.pop_back();
647 BVHNode *node = element->bvhNode;
648 Q_ASSERT(node->type == BVHNode::Leaf);
649 Q_ASSERT(node->element == element);
650 if (!element->processed) {
651 if (!intersectNodes(elements, node, m_bvh.root))
652 element->processed = true;
653 }
654 }
655
656 m_bvh.free(); // The bounding volume hierarchy is not needed anymore.
657}
658
659bool PathSimplifier::connectElements()
660{
661 Q_ASSERT(!m_elements.isEmpty());
662 QDataBuffer<Event> events(m_elements.size() * 2);
663 for (int i = 0; i < m_elements.size(); ++i) {
664 Element *element = m_elements.at(i);
665 element->next = element->previous = nullptr;
666 element->winding = 0;
667 element->edgeNode = nullptr;
668 const QPoint &u = m_points->at(element->indices[0]);
669 const QPoint &v = m_points->at(element->indices[element->degree]);
670 if (u != v) {
671 element->pointingUp = element->originallyPointingUp = v < u;
672
673 Event event;
674 event.element = element;
675 event.point = u;
676 event.type = element->pointingUp ? Event::Lower : Event::Upper;
677 events.add(event);
678 event.point = v;
679 event.type = element->pointingUp ? Event::Upper : Event::Lower;
680 events.add(event);
681 }
682 }
683 QVarLengthArray<Element *, 8> orderedElements;
684 if (!events.isEmpty())
685 sortEvents(events.data(), events.size());
686 while (!events.isEmpty()) {
687 const Event *event = &events.last();
688 QPoint eventPoint = event->point;
689
690 // Find all elements passing through the event point.
691 QPair<RBNode *, RBNode *> bounds = outerBounds(eventPoint);
692
693 // Special case: single element above and single element below event point.
694 int eventCount = events.size();
695 if (event->type == Event::Lower && eventCount > 2) {
696 QPair<RBNode *, RBNode *> range;
697 range.first = bounds.first ? m_elementList.next(bounds.first)
698 : m_elementList.front(m_elementList.root);
699 range.second = bounds.second ? m_elementList.previous(bounds.second)
700 : m_elementList.back(m_elementList.root);
701
702 const Event *event2 = &events.at(eventCount - 2);
703 const Event *event3 = &events.at(eventCount - 3);
704 Q_ASSERT(event2->point == eventPoint); // There are always at least two events at a point.
705 if (range.first == range.second && event2->type == Event::Upper && event3->point != eventPoint) {
706 Element *element = event->element;
707 Element *element2 = event2->element;
708 element->edgeNode->data = event2->element;
709 element2->edgeNode = element->edgeNode;
710 element->edgeNode = nullptr;
711
712 events.pop_back();
713 events.pop_back();
714
715 if (element2->pointingUp != element->pointingUp)
716 element2->flip();
717 element2->winding = element->winding;
718 int winding = element->winding;
719 if (element->originallyPointingUp)
720 ++winding;
721 if (winding == 0 || winding == 1) {
722 if (element->pointingUp) {
723 element->previous = event2->element;
724 element2->next = event->element;
725 } else {
726 element->next = event2->element;
727 element2->previous = event->element;
728 }
729 }
730 continue;
731 }
732 }
733 orderedElements.clear();
734
735 // First, find the ones above the event point.
736 if (m_elementList.root) {
737 RBNode *current = bounds.first ? m_elementList.next(bounds.first)
738 : m_elementList.front(m_elementList.root);
739 while (current != bounds.second) {
740 Element *element = current->data;
741 Q_ASSERT(element->edgeNode == current);
742 int winding = element->winding;
743 if (element->originallyPointingUp)
744 ++winding;
745 const QPoint &lower = m_points->at(element->lowerIndex());
746 if (lower == eventPoint) {
747 if (winding == 0 || winding == 1)
748 orderedElements.append(current->data);
749 } else {
750 // The element is passing through 'event.point'.
751 Q_ASSERT(m_points->at(element->upperIndex()) != eventPoint);
752 Q_ASSERT(element->degree == Element::Line);
753 // Split the line.
754 Element *eventElement = event->element;
755 int indexIndex = (event->type == Event::Upper) == eventElement->pointingUp
756 ? eventElement->degree : 0;
757 quint32 pointIndex = eventElement->indices[indexIndex];
758 Q_ASSERT(eventPoint == m_points->at(pointIndex));
759
760 Element *upperElement = m_elementAllocator.newElement();
761 *upperElement = *element;
762 upperElement->lowerIndex() = element->upperIndex() = pointIndex;
763 upperElement->edgeNode = nullptr;
764 element->next = element->previous = nullptr;
765 if (upperElement->next)
766 upperElement->next->previous = upperElement;
767 else if (upperElement->previous)
768 upperElement->previous->next = upperElement;
769 if (element->pointingUp != element->originallyPointingUp)
770 element->flip();
771 if (winding == 0 || winding == 1)
772 orderedElements.append(upperElement);
773 m_elements.add(upperElement);
774 }
775 current = m_elementList.next(current);
776 }
777 }
778 while (!events.isEmpty() && events.last().point == eventPoint) {
779 event = &events.last();
780 if (event->type == Event::Upper) {
781 Q_ASSERT(event->point == m_points->at(event->element->upperIndex()));
782 RBNode *left = findElementLeftOf(event->element, bounds);
783 RBNode *node = m_elementList.newNode();
784 node->data = event->element;
785 Q_ASSERT(event->element->edgeNode == nullptr);
786 event->element->edgeNode = node;
787 m_elementList.attachAfter(left, node);
788 } else {
789 Q_ASSERT(event->type == Event::Lower);
790 Q_ASSERT(event->point == m_points->at(event->element->lowerIndex()));
791 Element *element = event->element;
792 Q_ASSERT(element->edgeNode);
793 m_elementList.deleteNode(element->edgeNode);
794 Q_ASSERT(element->edgeNode == nullptr);
795 }
796 events.pop_back();
797 }
798
799 if (m_elementList.root) {
800 RBNode *current = bounds.first ? m_elementList.next(bounds.first)
801 : m_elementList.front(m_elementList.root);
802 int winding = bounds.first ? bounds.first->data->winding : 0;
803
804 // Calculate winding numbers and flip elements if necessary.
805 while (current != bounds.second) {
806 Element *element = current->data;
807 Q_ASSERT(element->edgeNode == current);
808 int ccw = winding & 1;
809 Q_ASSERT(element->pointingUp == element->originallyPointingUp);
810 if (element->originallyPointingUp) {
811 --winding;
812 } else {
813 ++winding;
814 ccw ^= 1;
815 }
816 element->winding = winding;
817 if (ccw == 0)
818 element->flip();
819 current = m_elementList.next(current);
820 }
821
822 // Pick elements with correct winding number.
823 current = bounds.second ? m_elementList.previous(bounds.second)
824 : m_elementList.back(m_elementList.root);
825 while (current != bounds.first) {
826 Element *element = current->data;
827 Q_ASSERT(element->edgeNode == current);
828 Q_ASSERT(m_points->at(element->upperIndex()) == eventPoint);
829 int winding = element->winding;
830 if (element->originallyPointingUp)
831 ++winding;
832 if (winding == 0 || winding == 1)
833 orderedElements.append(current->data);
834 current = m_elementList.previous(current);
835 }
836 }
837
838 if (!orderedElements.isEmpty()) {
839 if (orderedElements.size() & 1) // Unexpected path structure
840 return false;
841 int i = 0;
842 Element *firstElement = orderedElements.at(0);
843 if (m_points->at(firstElement->indices[0]) != eventPoint) {
844 orderedElements.append(firstElement);
845 i = 1;
846 }
847 for (; i < orderedElements.size(); i += 2) {
848 Q_ASSERT(i + 1 < orderedElements.size());
849 Element *next = orderedElements.at(i);
850 Element *previous = orderedElements.at(i + 1);
851 Q_ASSERT(next->previous == nullptr);
852 Q_ASSERT(previous->next == nullptr);
853 next->previous = previous;
854 previous->next = next;
855 }
856 }
857 }
858#ifndef QT_NO_DEBUG
859 for (int i = 0; i < m_elements.size(); ++i) {
860 const Element *element = m_elements.at(i);
861 Q_ASSERT(element->next == nullptr || element->next->previous == element);
862 Q_ASSERT(element->previous == nullptr || element->previous->next == element);
863 Q_ASSERT((element->next == nullptr) == (element->previous == nullptr));
864 }
865#endif
866 return true;
867}
868
869void PathSimplifier::fillIndices()
870{
871 for (int i = 0; i < m_elements.size(); ++i)
872 m_elements.at(i)->processed = false;
873 for (int i = 0; i < m_elements.size(); ++i) {
874 Element *element = m_elements.at(i);
875 if (element->processed || element->next == nullptr)
876 continue;
877 do {
878 m_indices->add(element->indices[0]);
879 switch (element->degree) {
880 case Element::Quadratic:
881 {
882 QPoint pts[] = {
883 m_points->at(element->indices[0]),
884 m_points->at(element->indices[1]),
885 m_points->at(element->indices[2])
886 };
887 subDivQuadratic(pts[0], pts[1], pts[2]);
888 }
889 break;
890 case Element::Cubic:
891 {
892 QPoint pts[] = {
893 m_points->at(element->indices[0]),
894 m_points->at(element->indices[1]),
895 m_points->at(element->indices[2]),
896 m_points->at(element->indices[3])
897 };
898 subDivCubic(pts[0], pts[1], pts[2], pts[3]);
899 }
900 break;
901 default:
902 break;
903 }
904 Q_ASSERT(element->next);
905 element->processed = true;
906 element = element->next;
907 } while (element != m_elements.at(i));
908 m_indices->add(Q_TRIANGULATE_END_OF_POLYGON);
909 }
910}
911
912PathSimplifier::BVHNode *PathSimplifier::buildTree(Element **elements, int elementCount)
913{
914 Q_ASSERT(elementCount > 0);
915 BVHNode *node = m_bvh.newNode();
916 if (elementCount == 1) {
917 Element *element = *elements;
918 element->bvhNode = node;
919 node->type = BVHNode::Leaf;
920 node->element = element;
921 node->minimum = node->maximum = m_points->at(element->indices[0]);
922 for (int i = 1; i <= element->degree; ++i) {
923 const QPoint &p = m_points->at(element->indices[i]);
924 node->minimum.rx() = qMin(node->minimum.x(), p.x());
925 node->minimum.ry() = qMin(node->minimum.y(), p.y());
926 node->maximum.rx() = qMax(node->maximum.x(), p.x());
927 node->maximum.ry() = qMax(node->maximum.y(), p.y());
928 }
929 return node;
930 }
931
932 node->type = BVHNode::Split;
933
935 minimum = maximum = elements[0]->middle;
936
937 for (int i = 1; i < elementCount; ++i) {
938 const QPoint &p = elements[i]->middle;
939 minimum.rx() = qMin(minimum.x(), p.x());
940 minimum.ry() = qMin(minimum.y(), p.y());
941 maximum.rx() = qMax(maximum.x(), p.x());
942 maximum.ry() = qMax(maximum.y(), p.y());
943 }
944
945 int comp, pivot;
946 if (maximum.x() - minimum.x() > maximum.y() - minimum.y()) {
947 comp = 0;
948 pivot = (maximum.x() + minimum.x()) >> 1;
949 } else {
950 comp = 1;
951 pivot = (maximum.y() + minimum.y()) >> 1;
952 }
953
954 int lo = 0;
955 int hi = elementCount - 1;
956 while (lo < hi) {
957 while (lo < hi && (&elements[lo]->middle.rx())[comp] <= pivot)
958 ++lo;
959 while (lo < hi && (&elements[hi]->middle.rx())[comp] > pivot)
960 --hi;
961 if (lo < hi)
962 qSwap(elements[lo], elements[hi]);
963 }
964
965 if (lo == elementCount) {
966 // All points are the same.
967 Q_ASSERT(minimum.x() == maximum.x() && minimum.y() == maximum.y());
968 lo = elementCount >> 1;
969 }
970
971 node->left = buildTree(elements, lo);
972 node->right = buildTree(elements + lo, elementCount - lo);
973
974 const BVHNode *left = node->left;
975 const BVHNode *right = node->right;
976 node->minimum.rx() = qMin(left->minimum.x(), right->minimum.x());
977 node->minimum.ry() = qMin(left->minimum.y(), right->minimum.y());
978 node->maximum.rx() = qMax(left->maximum.x(), right->maximum.x());
979 node->maximum.ry() = qMax(left->maximum.y(), right->maximum.y());
980
981 return node;
982}
983
984bool PathSimplifier::intersectNodes(QDataBuffer<Element *> &elements, BVHNode *elementNode,
985 BVHNode *treeNode)
986{
987 if (elementNode->minimum.x() >= treeNode->maximum.x()
988 || elementNode->minimum.y() >= treeNode->maximum.y()
989 || elementNode->maximum.x() <= treeNode->minimum.x()
990 || elementNode->maximum.y() <= treeNode->minimum.y())
991 {
992 return false;
993 }
994
995 Q_ASSERT(elementNode->type == BVHNode::Leaf);
996 Element *element = elementNode->element;
997 Q_ASSERT(!element->processed);
998
999 if (treeNode->type == BVHNode::Leaf) {
1000 Element *nodeElement = treeNode->element;
1001 if (!nodeElement->processed)
1002 return false;
1003
1004 if (treeNode->element == elementNode->element)
1005 return false;
1006
1007 if (equalElements(treeNode->element, elementNode->element))
1008 return false; // element doesn't split itself.
1009
1010 if (element->degree == Element::Line && nodeElement->degree == Element::Line) {
1011 const QPoint &u1 = m_points->at(element->indices[0]);
1012 const QPoint &u2 = m_points->at(element->indices[1]);
1013 const QPoint &v1 = m_points->at(nodeElement->indices[0]);
1014 const QPoint &v2 = m_points->at(nodeElement->indices[1]);
1015 IntersectionPoint intersection = intersectionPoint(u1, u2, v1, v2);
1016 if (!intersection.isValid())
1017 return false;
1018
1019 Q_ASSERT(intersection.x.integer >= qMin(u1.x(), u2.x()));
1020 Q_ASSERT(intersection.y.integer >= qMin(u1.y(), u2.y()));
1021 Q_ASSERT(intersection.x.integer >= qMin(v1.x(), v2.x()));
1022 Q_ASSERT(intersection.y.integer >= qMin(v1.y(), v2.y()));
1023
1024 Q_ASSERT(intersection.x.integer <= qMax(u1.x(), u2.x()));
1025 Q_ASSERT(intersection.y.integer <= qMax(u1.y(), u2.y()));
1026 Q_ASSERT(intersection.x.integer <= qMax(v1.x(), v2.x()));
1027 Q_ASSERT(intersection.y.integer <= qMax(v1.y(), v2.y()));
1028
1029 m_points->add(intersection.round());
1030 splitLineAt(elements, treeNode, m_points->size() - 1, !intersection.isAccurate());
1031 return splitLineAt(elements, elementNode, m_points->size() - 1, false);
1032 } else {
1033 QVarLengthArray<QPoint, 12> axes;
1034 appendSeparatingAxes(axes, elementNode->element);
1035 appendSeparatingAxes(axes, treeNode->element);
1036 for (int i = 0; i < axes.size(); ++i) {
1037 QPair<int, int> range1 = calculateSeparatingAxisRange(axes.at(i), elementNode->element);
1038 QPair<int, int> range2 = calculateSeparatingAxisRange(axes.at(i), treeNode->element);
1039 if (range1.first >= range2.second || range1.second <= range2.first) {
1040 return false; // Separating axis found.
1041 }
1042 }
1043 // Bounding areas overlap.
1044 if (nodeElement->degree > Element::Line)
1045 splitCurve(elements, treeNode);
1046 if (element->degree > Element::Line) {
1047 splitCurve(elements, elementNode);
1048 } else {
1049 // The element was not split, so it can be processed further.
1050 if (intersectNodes(elements, elementNode, treeNode->left))
1051 return true;
1052 if (intersectNodes(elements, elementNode, treeNode->right))
1053 return true;
1054 return false;
1055 }
1056 return true;
1057 }
1058 } else {
1059 if (intersectNodes(elements, elementNode, treeNode->left))
1060 return true;
1061 if (intersectNodes(elements, elementNode, treeNode->right))
1062 return true;
1063 return false;
1064 }
1065}
1066
1067bool PathSimplifier::equalElements(const Element *e1, const Element *e2)
1068{
1069 Q_ASSERT(e1 != e2);
1070 if (e1->degree != e2->degree)
1071 return false;
1072
1073 // Possibly equal and in the same direction.
1074 bool equalSame = true;
1075 for (int i = 0; i <= e1->degree; ++i)
1076 equalSame &= m_points->at(e1->indices[i]) == m_points->at(e2->indices[i]);
1077
1078 // Possibly equal and in opposite directions.
1079 bool equalOpposite = true;
1080 for (int i = 0; i <= e1->degree; ++i)
1081 equalOpposite &= m_points->at(e1->indices[e1->degree - i]) == m_points->at(e2->indices[i]);
1082
1083 return equalSame || equalOpposite;
1084}
1085
1086bool PathSimplifier::splitLineAt(QDataBuffer<Element *> &elements, BVHNode *node,
1087 quint32 pointIndex, bool processAgain)
1088{
1089 Q_ASSERT(node->type == BVHNode::Leaf);
1090 Element *element = node->element;
1091 Q_ASSERT(element->degree == Element::Line);
1092 const QPoint &u = m_points->at(element->indices[0]);
1093 const QPoint &v = m_points->at(element->indices[1]);
1094 const QPoint &p = m_points->at(pointIndex);
1095 if (u == p || v == p)
1096 return false; // No split needed.
1097
1098 if (processAgain)
1099 element->processed = false; // Needs to be processed again.
1100
1101 Element *first = node->element;
1102 Element *second = m_elementAllocator.newElement();
1103 *second = *first;
1104 first->indices[1] = second->indices[0] = pointIndex;
1105 first->middle.rx() = (u.x() + p.x()) >> 1;
1106 first->middle.ry() = (u.y() + p.y()) >> 1;
1107 second->middle.rx() = (v.x() + p.x()) >> 1;
1108 second->middle.ry() = (v.y() + p.y()) >> 1;
1109 m_elements.add(second);
1110
1111 BVHNode *left = m_bvh.newNode();
1112 BVHNode *right = m_bvh.newNode();
1113 left->type = right->type = BVHNode::Leaf;
1114 left->element = first;
1115 right->element = second;
1116 left->minimum = right->minimum = node->minimum;
1117 left->maximum = right->maximum = node->maximum;
1118 if (u.x() < v.x())
1119 left->maximum.rx() = right->minimum.rx() = p.x();
1120 else
1121 left->minimum.rx() = right->maximum.rx() = p.x();
1122 if (u.y() < v.y())
1123 left->maximum.ry() = right->minimum.ry() = p.y();
1124 else
1125 left->minimum.ry() = right->maximum.ry() = p.y();
1126 left->element->bvhNode = left;
1127 right->element->bvhNode = right;
1128
1129 node->type = BVHNode::Split;
1130 node->left = left;
1131 node->right = right;
1132
1133 if (!first->processed) {
1134 elements.add(left->element);
1135 elements.add(right->element);
1136 }
1137 return true;
1138}
1139
1140void PathSimplifier::appendSeparatingAxes(QVarLengthArray<QPoint, 12> &axes, Element *element)
1141{
1142 switch (element->degree) {
1143 case Element::Cubic:
1144 {
1145 const QPoint &u = m_points->at(element->indices[0]);
1146 const QPoint &v = m_points->at(element->indices[1]);
1147 const QPoint &w = m_points->at(element->indices[2]);
1148 const QPoint &q = m_points->at(element->indices[3]);
1149 QPoint ns[] = {
1150 QPoint(u.y() - v.y(), v.x() - u.x()),
1151 QPoint(v.y() - w.y(), w.x() - v.x()),
1152 QPoint(w.y() - q.y(), q.x() - w.x()),
1153 QPoint(q.y() - u.y(), u.x() - q.x()),
1154 QPoint(u.y() - w.y(), w.x() - u.x()),
1155 QPoint(v.y() - q.y(), q.x() - v.x())
1156 };
1157 for (int i = 0; i < 6; ++i) {
1158 if (ns[i].x() || ns[i].y())
1159 axes.append(ns[i]);
1160 }
1161 }
1162 break;
1163 case Element::Quadratic:
1164 {
1165 const QPoint &u = m_points->at(element->indices[0]);
1166 const QPoint &v = m_points->at(element->indices[1]);
1167 const QPoint &w = m_points->at(element->indices[2]);
1168 QPoint ns[] = {
1169 QPoint(u.y() - v.y(), v.x() - u.x()),
1170 QPoint(v.y() - w.y(), w.x() - v.x()),
1171 QPoint(w.y() - u.y(), u.x() - w.x())
1172 };
1173 for (int i = 0; i < 3; ++i) {
1174 if (ns[i].x() || ns[i].y())
1175 axes.append(ns[i]);
1176 }
1177 }
1178 break;
1179 case Element::Line:
1180 {
1181 const QPoint &u = m_points->at(element->indices[0]);
1182 const QPoint &v = m_points->at(element->indices[1]);
1183 QPoint n(u.y() - v.y(), v.x() - u.x());
1184 if (n.x() || n.y())
1185 axes.append(n);
1186 }
1187 break;
1188 default:
1189 Q_ASSERT_X(0, "QSGPathSimplifier::appendSeparatingAxes", "Unexpected element type.");
1190 break;
1191 }
1192}
1193
1194QPair<int, int> PathSimplifier::calculateSeparatingAxisRange(const QPoint &axis, Element *element)
1195{
1196 QPair<int, int> range(0x7fffffff, -0x7fffffff);
1197 for (int i = 0; i <= element->degree; ++i) {
1198 const QPoint &p = m_points->at(element->indices[i]);
1199 int dist = dot(axis, p);
1200 range.first = qMin(range.first, dist);
1201 range.second = qMax(range.second, dist);
1202 }
1203 return range;
1204}
1205
1206void PathSimplifier::splitCurve(QDataBuffer<Element *> &elements, BVHNode *node)
1207{
1208 Q_ASSERT(node->type == BVHNode::Leaf);
1209
1210 Element *first = node->element;
1211 Element *second = m_elementAllocator.newElement();
1212 *second = *first;
1213 m_elements.add(second);
1214 Q_ASSERT(first->degree > Element::Line);
1215
1216 bool accurate = true;
1217 const QPoint &u = m_points->at(first->indices[0]);
1218 const QPoint &v = m_points->at(first->indices[1]);
1219 const QPoint &w = m_points->at(first->indices[2]);
1220
1221 if (first->degree == Element::Quadratic) {
1222 QPoint pts[3];
1223 accurate = splitQuadratic(u, v, w, pts);
1224 int pointIndex = m_points->size();
1225 m_points->add(pts[1]);
1226 accurate &= setElementToQuadratic(first, first->indices[0], pts[0], pointIndex);
1227 accurate &= setElementToQuadratic(second, pointIndex, pts[2], second->indices[2]);
1228 } else {
1229 Q_ASSERT(first->degree == Element::Cubic);
1230 const QPoint &q = m_points->at(first->indices[3]);
1231 QPoint pts[5];
1232 accurate = splitCubic(u, v, w, q, pts);
1233 int pointIndex = m_points->size();
1234 m_points->add(pts[2]);
1235 accurate &= setElementToCubic(first, first->indices[0], pts[0], pts[1], pointIndex);
1236 accurate &= setElementToCubic(second, pointIndex, pts[3], pts[4], second->indices[3]);
1237 }
1238
1239 if (!accurate)
1240 first->processed = second->processed = false; // Needs to be processed again.
1241
1242 BVHNode *left = m_bvh.newNode();
1243 BVHNode *right = m_bvh.newNode();
1244 left->type = right->type = BVHNode::Leaf;
1245 left->element = first;
1246 right->element = second;
1247
1248 left->minimum.rx() = left->minimum.ry() = right->minimum.rx() = right->minimum.ry() = INT_MAX;
1249 left->maximum.rx() = left->maximum.ry() = right->maximum.rx() = right->maximum.ry() = INT_MIN;
1250
1251 for (int i = 0; i <= first->degree; ++i) {
1252 QPoint &p = m_points->at(first->indices[i]);
1253 left->minimum.rx() = qMin(left->minimum.x(), p.x());
1254 left->minimum.ry() = qMin(left->minimum.y(), p.y());
1255 left->maximum.rx() = qMax(left->maximum.x(), p.x());
1256 left->maximum.ry() = qMax(left->maximum.y(), p.y());
1257 }
1258 for (int i = 0; i <= second->degree; ++i) {
1259 QPoint &p = m_points->at(second->indices[i]);
1260 right->minimum.rx() = qMin(right->minimum.x(), p.x());
1261 right->minimum.ry() = qMin(right->minimum.y(), p.y());
1262 right->maximum.rx() = qMax(right->maximum.x(), p.x());
1263 right->maximum.ry() = qMax(right->maximum.y(), p.y());
1264 }
1265 left->element->bvhNode = left;
1266 right->element->bvhNode = right;
1267
1268 node->type = BVHNode::Split;
1269 node->left = left;
1270 node->right = right;
1271
1272 if (!first->processed) {
1273 elements.add(left->element);
1274 elements.add(right->element);
1275 }
1276}
1277
1278bool PathSimplifier::setElementToQuadratic(Element *element, quint32 pointIndex1,
1279 const QPoint &ctrl, quint32 pointIndex2)
1280{
1281 const QPoint &p1 = m_points->at(pointIndex1);
1282 const QPoint &p2 = m_points->at(pointIndex2);
1283 if (flattenQuadratic(p1, ctrl, p2)) {
1284 // Insert line.
1285 element->degree = Element::Line;
1286 element->indices[0] = pointIndex1;
1287 element->indices[1] = pointIndex2;
1288 element->middle.rx() = (p1.x() + p2.x()) >> 1;
1289 element->middle.ry() = (p1.y() + p2.y()) >> 1;
1290 return false;
1291 } else {
1292 // Insert bezier.
1293 element->degree = Element::Quadratic;
1294 element->indices[0] = pointIndex1;
1295 element->indices[1] = m_points->size();
1296 element->indices[2] = pointIndex2;
1297 element->middle.rx() = (p1.x() + ctrl.x() + p2.x()) / 3;
1298 element->middle.ry() = (p1.y() + ctrl.y() + p2.y()) / 3;
1299 m_points->add(ctrl);
1300 return true;
1301 }
1302}
1303
1304bool PathSimplifier::setElementToCubic(Element *element, quint32 pointIndex1, const QPoint &v,
1305 const QPoint &w, quint32 pointIndex2)
1306{
1307 const QPoint &u = m_points->at(pointIndex1);
1308 const QPoint &q = m_points->at(pointIndex2);
1309 if (flattenCubic(u, v, w, q)) {
1310 // Insert line.
1311 element->degree = Element::Line;
1312 element->indices[0] = pointIndex1;
1313 element->indices[1] = pointIndex2;
1314 element->middle.rx() = (u.x() + q.x()) >> 1;
1315 element->middle.ry() = (u.y() + q.y()) >> 1;
1316 return false;
1317 } else {
1318 // Insert bezier.
1319 element->degree = Element::Cubic;
1320 element->indices[0] = pointIndex1;
1321 element->indices[1] = m_points->size();
1322 element->indices[2] = m_points->size() + 1;
1323 element->indices[3] = pointIndex2;
1324 element->middle.rx() = (u.x() + v.x() + w.x() + q.x()) >> 2;
1325 element->middle.ry() = (u.y() + v.y() + w.y() + q.y()) >> 2;
1326 m_points->add(v);
1327 m_points->add(w);
1328 return true;
1329 }
1330}
1331
1332void PathSimplifier::setElementToCubicAndSimplify(Element *element, quint32 pointIndex1,
1333 const QPoint &v, const QPoint &w,
1334 quint32 pointIndex2)
1335{
1336 const QPoint &u = m_points->at(pointIndex1);
1337 const QPoint &q = m_points->at(pointIndex2);
1338 if (flattenCubic(u, v, w, q)) {
1339 // Insert line.
1340 element->degree = Element::Line;
1341 element->indices[0] = pointIndex1;
1342 element->indices[1] = pointIndex2;
1343 element->middle.rx() = (u.x() + q.x()) >> 1;
1344 element->middle.ry() = (u.y() + q.y()) >> 1;
1345 return;
1346 }
1347
1348 bool intersecting = (u == q) || intersectionPoint(u, v, w, q).isValid();
1349 if (!intersecting) {
1350 // Insert bezier.
1351 element->degree = Element::Cubic;
1352 element->indices[0] = pointIndex1;
1353 element->indices[1] = m_points->size();
1354 element->indices[2] = m_points->size() + 1;
1355 element->indices[3] = pointIndex2;
1356 element->middle.rx() = (u.x() + v.x() + w.x() + q.x()) >> 2;
1357 element->middle.ry() = (u.y() + v.y() + w.y() + q.y()) >> 2;
1358 m_points->add(v);
1359 m_points->add(w);
1360 return;
1361 }
1362
1363 QPoint pts[5];
1364 splitCubic(u, v, w, q, pts);
1365 int pointIndex = m_points->size();
1366 m_points->add(pts[2]);
1367 Element *element2 = m_elementAllocator.newElement();
1368 m_elements.add(element2);
1369 setElementToCubicAndSimplify(element, pointIndex1, pts[0], pts[1], pointIndex);
1370 setElementToCubicAndSimplify(element2, pointIndex, pts[3], pts[4], pointIndex2);
1371}
1372
1373PathSimplifier::RBNode *PathSimplifier::findElementLeftOf(const Element *element,
1374 const QPair<RBNode *, RBNode *> &bounds)
1375{
1376 if (!m_elementList.root)
1377 return nullptr;
1378 RBNode *current = bounds.first;
1379 Q_ASSERT(!current || !elementIsLeftOf(element, current->data));
1380 if (!current)
1381 current = m_elementList.front(m_elementList.root);
1382 Q_ASSERT(current);
1383 RBNode *result = nullptr;
1384 while (current != bounds.second && !elementIsLeftOf(element, current->data)) {
1385 result = current;
1386 current = m_elementList.next(current);
1387 }
1388 return result;
1389}
1390
1391bool PathSimplifier::elementIsLeftOf(const Element *left, const Element *right)
1392{
1393 const QPoint &leftU = m_points->at(left->upperIndex());
1394 const QPoint &leftL = m_points->at(left->lowerIndex());
1395 const QPoint &rightU = m_points->at(right->upperIndex());
1396 const QPoint &rightL = m_points->at(right->lowerIndex());
1397 Q_ASSERT(leftL >= rightU && rightL >= leftU);
1398 if (leftU.x() < qMin(rightL.x(), rightU.x()))
1399 return true;
1400 if (leftU.x() > qMax(rightL.x(), rightU.x()))
1401 return false;
1402 int d = pointDistanceFromLine(leftU, rightL, rightU);
1403 // d < 0: left, d > 0: right, d == 0: on top
1404 if (d == 0) {
1405 d = pointDistanceFromLine(leftL, rightL, rightU);
1406 if (d == 0) {
1407 if (right->degree > Element::Line) {
1408 d = pointDistanceFromLine(leftL, rightL, m_points->at(right->indices[1]));
1409 if (d == 0)
1410 d = pointDistanceFromLine(leftL, rightL, m_points->at(right->indices[2]));
1411 } else if (left->degree > Element::Line) {
1412 d = pointDistanceFromLine(m_points->at(left->indices[1]), rightL, rightU);
1413 if (d == 0)
1414 d = pointDistanceFromLine(m_points->at(left->indices[2]), rightL, rightU);
1415 }
1416 }
1417 }
1418 return d < 0;
1419}
1420
1421QPair<PathSimplifier::RBNode *, PathSimplifier::RBNode *> PathSimplifier::outerBounds(const QPoint &point)
1422{
1423 RBNode *current = m_elementList.root;
1424 QPair<RBNode *, RBNode *> result(nullptr, nullptr);
1425
1426 while (current) {
1427 const Element *element = current->data;
1428 Q_ASSERT(element->edgeNode == current);
1429 const QPoint &v1 = m_points->at(element->lowerIndex());
1430 const QPoint &v2 = m_points->at(element->upperIndex());
1431 Q_ASSERT(point >= v2 && point <= v1);
1432 if (point == v1 || point == v2)
1433 break;
1434 int d = pointDistanceFromLine(point, v1, v2);
1435 if (d == 0) {
1436 if (element->degree == Element::Line)
1437 break;
1438 d = pointDistanceFromLine(point, v1, m_points->at(element->indices[1]));
1439 if (d == 0)
1440 d = pointDistanceFromLine(point, v1, m_points->at(element->indices[2]));
1441 Q_ASSERT(d != 0);
1442 }
1443 if (d < 0) {
1444 result.second = current;
1445 current = current->left;
1446 } else {
1447 result.first = current;
1448 current = current->right;
1449 }
1450 }
1451
1452 if (!current)
1453 return result;
1454
1455 RBNode *mid = current;
1456
1457 current = mid->left;
1458 while (current) {
1459 const Element *element = current->data;
1460 Q_ASSERT(element->edgeNode == current);
1461 const QPoint &v1 = m_points->at(element->lowerIndex());
1462 const QPoint &v2 = m_points->at(element->upperIndex());
1463 Q_ASSERT(point >= v2 && point <= v1);
1464 bool equal = (point == v1 || point == v2);
1465 if (!equal) {
1466 int d = pointDistanceFromLine(point, v1, v2);
1467 Q_ASSERT(d >= 0);
1468 equal = (d == 0 && element->degree == Element::Line);
1469 }
1470 if (equal) {
1471 current = current->left;
1472 } else {
1473 result.first = current;
1474 current = current->right;
1475 }
1476 }
1477
1478 current = mid->right;
1479 while (current) {
1480 const Element *element = current->data;
1481 Q_ASSERT(element->edgeNode == current);
1482 const QPoint &v1 = m_points->at(element->lowerIndex());
1483 const QPoint &v2 = m_points->at(element->upperIndex());
1484 Q_ASSERT(point >= v2 && point <= v1);
1485 bool equal = (point == v1 || point == v2);
1486 if (!equal) {
1487 int d = pointDistanceFromLine(point, v1, v2);
1488 Q_ASSERT(d <= 0);
1489 equal = (d == 0 && element->degree == Element::Line);
1490 }
1491 if (equal) {
1492 current = current->right;
1493 } else {
1494 result.second = current;
1495 current = current->left;
1496 }
1497 }
1498
1499 return result;
1500}
1501
1502inline bool PathSimplifier::flattenQuadratic(const QPoint &u, const QPoint &v, const QPoint &w)
1503{
1504 QPoint deltas[2] = { v - u, w - v };
1505 int d = qAbs(cross(deltas[0], deltas[1]));
1506 int l = qAbs(deltas[0].x()) + qAbs(deltas[0].y()) + qAbs(deltas[1].x()) + qAbs(deltas[1].y());
1507 return d < (Q_FIXED_POINT_SCALE * Q_FIXED_POINT_SCALE * 3 / 2) || l <= Q_FIXED_POINT_SCALE * 2;
1508}
1509
1510inline bool PathSimplifier::flattenCubic(const QPoint &u, const QPoint &v,
1511 const QPoint &w, const QPoint &q)
1512{
1513 QPoint deltas[] = { v - u, w - v, q - w, q - u };
1514 int d = qAbs(cross(deltas[0], deltas[1])) + qAbs(cross(deltas[1], deltas[2]))
1515 + qAbs(cross(deltas[0], deltas[3])) + qAbs(cross(deltas[3], deltas[2]));
1516 int l = qAbs(deltas[0].x()) + qAbs(deltas[0].y()) + qAbs(deltas[1].x()) + qAbs(deltas[1].y())
1517 + qAbs(deltas[2].x()) + qAbs(deltas[2].y());
1518 return d < (Q_FIXED_POINT_SCALE * Q_FIXED_POINT_SCALE * 3) || l <= Q_FIXED_POINT_SCALE * 2;
1519}
1520
1521inline bool PathSimplifier::splitQuadratic(const QPoint &u, const QPoint &v,
1522 const QPoint &w, QPoint *result)
1523{
1524 result[0] = u + v;
1525 result[2] = v + w;
1526 result[1] = result[0] + result[2];
1527 bool accurate = ((result[0].x() | result[0].y() | result[2].x() | result[2].y()) & 1) == 0
1528 && ((result[1].x() | result[1].y()) & 3) == 0;
1529 result[0].rx() >>= 1;
1530 result[0].ry() >>= 1;
1531 result[1].rx() >>= 2;
1532 result[1].ry() >>= 2;
1533 result[2].rx() >>= 1;
1534 result[2].ry() >>= 1;
1535 return accurate;
1536}
1537
1538inline bool PathSimplifier::splitCubic(const QPoint &u, const QPoint &v,
1539 const QPoint &w, const QPoint &q, QPoint *result)
1540{
1541 result[0] = u + v;
1542 result[2] = v + w;
1543 result[4] = w + q;
1544 result[1] = result[0] + result[2];
1545 result[3] = result[2] + result[4];
1546 result[2] = result[1] + result[3];
1547 bool accurate = ((result[0].x() | result[0].y() | result[4].x() | result[4].y()) & 1) == 0
1548 && ((result[1].x() | result[1].y() | result[3].x() | result[3].y()) & 3) == 0
1549 && ((result[2].x() | result[2].y()) & 7) == 0;
1550 result[0].rx() >>= 1;
1551 result[0].ry() >>= 1;
1552 result[1].rx() >>= 2;
1553 result[1].ry() >>= 2;
1554 result[2].rx() >>= 3;
1555 result[2].ry() >>= 3;
1556 result[3].rx() >>= 2;
1557 result[3].ry() >>= 2;
1558 result[4].rx() >>= 1;
1559 result[4].ry() >>= 1;
1560 return accurate;
1561}
1562
1563inline void PathSimplifier::subDivQuadratic(const QPoint &u, const QPoint &v, const QPoint &w)
1564{
1565 if (flattenQuadratic(u, v, w))
1566 return;
1567 QPoint pts[3];
1568 splitQuadratic(u, v, w, pts);
1569 subDivQuadratic(u, pts[0], pts[1]);
1570 m_indices->add(m_points->size());
1571 m_points->add(pts[1]);
1572 subDivQuadratic(pts[1], pts[2], w);
1573}
1574
1575inline void PathSimplifier::subDivCubic(const QPoint &u, const QPoint &v,
1576 const QPoint &w, const QPoint &q)
1577{
1578 if (flattenCubic(u, v, w, q))
1579 return;
1580 QPoint pts[5];
1581 splitCubic(u, v, w, q, pts);
1582 subDivCubic(u, pts[0], pts[1], pts[2]);
1583 m_indices->add(m_points->size());
1584 m_points->add(pts[2]);
1585 subDivCubic(pts[2], pts[3], pts[4], q);
1586}
1587
1588void PathSimplifier::sortEvents(Event *events, int count)
1589{
1590 // Bucket sort + insertion sort.
1591 Q_ASSERT(count > 0);
1592 QDataBuffer<Event> buffer(count);
1593 buffer.resize(count);
1594 QScopedArrayPointer<int> bins(new int[count]);
1595 int counts[0x101];
1596 memset(counts, 0, sizeof(counts));
1597
1598 int minimum, maximum;
1599 minimum = maximum = events[0].point.y();
1600 for (int i = 1; i < count; ++i) {
1601 minimum = qMin(minimum, events[i].point.y());
1602 maximum = qMax(maximum, events[i].point.y());
1603 }
1604
1605 for (int i = 0; i < count; ++i) {
1606 bins[i] = ((maximum - events[i].point.y()) << 8) / (maximum - minimum + 1);
1607 Q_ASSERT(bins[i] >= 0 && bins[i] < 0x100);
1608 ++counts[bins[i]];
1609 }
1610
1611 for (int i = 1; i < 0x100; ++i)
1612 counts[i] += counts[i - 1];
1613 counts[0x100] = counts[0xff];
1614 Q_ASSERT(counts[0x100] == count);
1615
1616 for (int i = 0; i < count; ++i)
1617 buffer.at(--counts[bins[i]]) = events[i];
1618
1619 int j = 0;
1620 for (int i = 0; i < 0x100; ++i) {
1621 for (; j < counts[i + 1]; ++j) {
1622 int k = j;
1623 while (k > 0 && (buffer.at(j) < events[k - 1])) {
1624 events[k] = events[k - 1];
1625 --k;
1626 }
1627 events[k] = buffer.at(j);
1628 }
1629 }
1630}
1631
1632void qSimplifyPath(const QVectorPath &path, QDataBuffer<QPoint> &vertices,
1633 QDataBuffer<quint32> &indices, const QTransform &matrix)
1634{
1635 PathSimplifier(path, vertices, indices, matrix);
1636}
1637
1638void qSimplifyPath(const QPainterPath &path, QDataBuffer<QPoint> &vertices,
1639 QDataBuffer<quint32> &indices, const QTransform &matrix)
1640{
1642}
1643
1644
1646
1647#undef Q_FIXED_POINT_SCALE
Definition lalr.h:136
\inmodule QtGui
ElementType
This enum describes the types of elements used to connect vertices in subpaths.
\inmodule QtCore\reentrant
Definition qpoint.h:25
constexpr int & rx() noexcept
Returns a reference to the x coordinate of this point.
Definition qpoint.h:155
constexpr int x() const noexcept
Returns the x coordinate of this point.
Definition qpoint.h:130
constexpr int y() const noexcept
Returns the y coordinate of this point.
Definition qpoint.h:135
The QTransform class specifies 2D transformations of a coordinate system.
Definition qtransform.h:20
constexpr float y() const noexcept
Returns the y coordinate of this point.
Definition qvectornd.h:671
constexpr float x() const noexcept
Returns the x coordinate of this point.
Definition qvectornd.h:670
The QWidget class is the base class of all user interface objects.
Definition qwidget.h:99
int x
the x coordinate of the widget relative to its parent including any window frame
Definition qwidget.h:109
QPixmap p2
QPixmap p1
[0]
short next
Definition keywords.cpp:445
void flip(QMatrix4x4 &matrix)
Definition qssgutils_p.h:78
QVector3D minimum(const QVector3D &v1, const QVector3D &v2) Q_DECL_NOTHROW
Definition qssgutils_p.h:54
QVector3D maximum(const QVector3D &v1, const QVector3D &v2) Q_DECL_NOTHROW
Definition qssgutils_p.h:55
Combined button and popup list for selecting options.
const int blockSize
@ Split
Definition qbezier.cpp:175
static void splitCubic(QCosmeticStroker::PointF *points)
int qRound(qfloat16 d) noexcept
Definition qfloat16.h:327
QT_BEGIN_NAMESPACE constexpr const T & qMin(const T &a, const T &b)
Definition qminmax.h:19
constexpr const T & qMax(const T &a, const T &b)
Definition qminmax.h:21
constexpr T qAbs(const T &t)
Definition qnumeric.h:328
GLint GLfloat GLfloat GLfloat v2
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLuint GLfloat GLfloat GLfloat GLfloat GLfloat z
GLint GLint GLint GLint GLint x
[0]
GLfloat GLfloat GLfloat w
[0]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLuint GLfloat GLfloat GLfloat GLfloat y1
GLuint GLuint end
GLuint GLfloat GLfloat GLfloat x1
GLenum GLenum GLsizei count
GLdouble GLdouble right
GLsizei range
GLenum GLuint buffer
GLint left
GLenum type
GLint GLfloat GLfloat v1
GLboolean GLboolean g
GLint first
GLfloat n
GLsizei GLenum const void * indices
GLint y
struct _cl_event * event
GLfixed GLfixed u2
GLfixed GLfixed GLfixed y2
GLuint GLenum matrix
GLfixed GLfixed x2
GLdouble GLdouble GLdouble GLdouble q
Definition qopenglext.h:259
GLsizei const GLchar *const * path
GLuint64EXT * result
[6]
GLfloat GLfloat p
[1]
GLfixed u1
const QVectorPath & qtVectorPathForPath(const QPainterPath &path)
static qreal dot(const QPointF &a, const QPointF &b)
void qSimplifyPath(const QVectorPath &path, QDataBuffer< QPoint > &vertices, QDataBuffer< quint32 > &indices, const QTransform &matrix)
bool operator<=(const QPoint &a, const QPoint &b)
bool operator>=(const QPoint &a, const QPoint &b)
#define Q_TRIANGULATE_END_OF_POLYGON
#define Q_FIXED_POINT_SCALE
bool operator>(const QPoint &a, const QPoint &b)
bool operator<(const QPoint &a, const QPoint &b)
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
#define Q_ASSERT_X(cond, x, msg)
Definition qrandom.cpp:48
static bool operator<(const QSettingsIniKey &k1, const QSettingsIniKey &k2)
static const struct TessellationWindingOrderTab ccw[]
QT_BEGIN_NAMESPACE constexpr void qSwap(T &value1, T &value2) noexcept(std::is_nothrow_swappable_v< T >)
Definition qswap.h:20
#define v1
static const QTextHtmlElement elements[Html_NumElements]
static quint64 gcd(quint64 x, quint64 y)
@ Q_PRIMITIVE_TYPE
Definition qtypeinfo.h:154
#define Q_DECLARE_TYPEINFO(TYPE, FLAGS)
Definition qtypeinfo.h:177
unsigned int quint32
Definition qtypes.h:50
unsigned int uint
Definition qtypes.h:34
long long qint64
Definition qtypes.h:60
double qreal
Definition qtypes.h:187
static bool equal(const QChar *a, int l, const char *b)
Definition qurlidna.cpp:338
std::uniform_real_distribution dist(1, 2.5)
[2]
QObject::connect nullptr
QDate d1(1995, 5, 17)
[0]
QDate d2(1995, 5, 20)
QSharedPointer< T > other(t)
[5]
Definition moc.h:23