14#include <qtextlayout.h>
15#include <qvarlengtharray.h>
18#include <private/qbezier_p.h>
19#include <private/qfontengine_p.h>
20#include <private/qnumeric_p.h>
21#include <private/qobject_p.h>
22#include <private/qpathclipper_p.h>
23#include <private/qstroker_p.h>
24#include <private/qtextengine_p.h>
31#include <performance.h>
42 if (
sizeof(qreal) >=
sizeof(
double))
43 return qIsFinite(c) && fabs(c) < 1e128;
45 return qIsFinite(c) && fabsf(
float(c)) < 1e16f;
50 return isValidCoord(p.x()) && isValidCoord(p.y());
55 return isValidCoord(r.x()) && isValidCoord(r.y()) && isValidCoord(r.width()) && isValidCoord(r.height());
69 QPointF* startPoint, QPointF *endPoint)
73 *startPoint = QPointF();
75 *endPoint = QPointF();
79 qreal w2 = r.width() / 2;
80 qreal h2 = r.height() / 2;
82 qreal angles[2] = { angle, angle + length };
83 QPointF *points[2] = { startPoint, endPoint };
85 for (
int i = 0; i < 2; ++i) {
89 qreal theta = angles[i] - 360 *
std::floor(angles[i] / 360);
92 int quadrant =
int(t);
95 t = qt_t_for_arc_angle(90 * t);
102 QBezier::coefficients(t, a, b, c, d);
103 QPointF p(a + b + c*QT_PATH_KAPPA, d + c + b*QT_PATH_KAPPA);
106 if (quadrant == 1 || quadrant == 2)
110 if (quadrant == 0 || quadrant == 1)
113 *points[i] = r.center() + QPointF(w2 * p.x(), h2 * p.y());
118static void qt_debug_path(
const QPainterPath &path)
120 const char *names[] = {
127 printf(
"\nQPainterPath: elementCount=%d\n", path.elementCount());
128 for (
int i=0; i<path.elementCount(); ++i) {
129 const QPainterPath::Element &e = path.elementAt(i);
130 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
131 printf(
" - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
374
375
376
377
378
381
382
383
384
385
388
389
390
391
392
395
396
397
398
399
400
401
402
405
406
407
408
409
410
411
412
415
416
417
418
419
420
423
424
425
426
427
428
431
432
433
434
435
436
437
440
441
442
443
444
445
448
449
450
451
452
453
454
457
458
459
460
461
462
463
464
467
468
469
470
471
472
473
476
477
478
479
480
481
483int QPainterPath::elementCount()
const
485 return d_ptr ? d_ptr->elements.size() : 0;
489
490
491
492
493
494
496QPainterPath::Element QPainterPath::elementAt(
int i)
const
499 Q_ASSERT(i >= 0 && i < elementCount());
500 return d_ptr->elements.at(i);
504
505
506
507
508
509
511void QPainterPath::setElementPositionAt(
int i, qreal x, qreal y)
514 Q_ASSERT(i >= 0 && i < elementCount());
516 QPainterPath::Element &e = d_ptr->elements[i];
523
524
525
526
527
530
531
532QPainterPath::QPainterPath()
noexcept
538
539
540
541
542
543
544QPainterPath::QPainterPath(
const QPainterPath &other)
545 : d_ptr(other.d_ptr ?
new QPainterPathPrivate(*other.d_ptr) :
nullptr)
550
551
552
553
554
555
556
559
560
561
563QPainterPath::QPainterPath(
const QPointF &startPoint)
564 : d_ptr(
new QPainterPathPrivate(startPoint))
569
570
571void QPainterPath::ensureData_helper()
573 Q_ASSERT(d_ptr ==
nullptr);
574 QPainterPathPrivate *data =
new QPainterPathPrivate;
575 data->elements.reserve(16);
576 QPainterPath::Element e = { 0, 0, QPainterPath::MoveToElement };
579 Q_ASSERT(d_ptr !=
nullptr);
583
584
585
586
587
588
589QPainterPath &QPainterPath::operator=(
const QPainterPath &other)
591 QPainterPath copy(other);
597
598
599
600
601
602
605
606
607
608
611
612
613QPainterPath::~QPainterPath()
619
620
621
622
623
624
625
626void QPainterPath::clear()
633 d_func()->elements.append( {0, 0, MoveToElement} );
637
638
639
640
641
642
643
644void QPainterPath::reserve(
int size)
647 if ((!d && size > 0) || (d && d->elements.capacity() < size)) {
650 d_func()->elements.reserve(size);
655
656
657
658
659
660int QPainterPath::capacity()
const
664 return d->elements.capacity();
670
671
672
673
674
675
676
677
678
679
680void QPainterPath::closeSubpath()
683 printf(
"QPainterPath::closeSubpath()\n");
693
694
695
696
697
698
699
702
703
704
705
706
707
708
709
710void QPainterPath::moveTo(
const QPointF &p)
713 printf(
"QPainterPath::moveTo() (%.2f,%.2f)\n", p.x(), p.y());
716 if (!hasValidCoords(p)) {
718 qWarning(
"QPainterPath::moveTo: Adding point with invalid coordinates, ignoring call");
726 QPainterPathPrivate *d = d_func();
727 Q_ASSERT(!d->elements.isEmpty());
729 d->require_moveTo =
false;
731 if (d->elements.constLast().type == MoveToElement) {
732 d->elements.last().x = p.x();
733 d->elements.last().y = p.y();
735 Element elm = { p.x(), p.y(), MoveToElement };
736 d->elements.append(elm);
738 d->cStart = d->elements.size() - 1;
742
743
744
745
746
747
748
751
752
753
754
755
756
757
758
759
760void QPainterPath::lineTo(
const QPointF &p)
763 printf(
"QPainterPath::lineTo() (%.2f,%.2f)\n", p.x(), p.y());
766 if (!hasValidCoords(p)) {
768 qWarning(
"QPainterPath::lineTo: Adding point with invalid coordinates, ignoring call");
776 QPainterPathPrivate *d = d_func();
777 Q_ASSERT(!d->elements.isEmpty());
779 if (p == QPointF(d->elements.constLast()))
781 Element elm = { p.x(), p.y(), LineToElement };
782 d->elements.append(elm);
784 d->convex = d->elements.size() == 3 || (d->elements.size() == 4 && d->isClosed());
788
789
790
791
792
793
794
795
796
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819void QPainterPath::cubicTo(
const QPointF &c1,
const QPointF &c2,
const QPointF &e)
822 printf(
"QPainterPath::cubicTo() (%.2f,%.2f), (%.2f,%.2f), (%.2f,%.2f)\n",
823 c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y());
826 if (!hasValidCoords(c1) || !hasValidCoords(c2) || !hasValidCoords(e)) {
828 qWarning(
"QPainterPath::cubicTo: Adding point with invalid coordinates, ignoring call");
836 QPainterPathPrivate *d = d_func();
837 Q_ASSERT(!d->elements.isEmpty());
842 if (d->elements.constLast() == c1 && c1 == c2 && c2 == e)
847 Element ce1 = { c1.x(), c1.y(), CurveToElement };
848 Element ce2 = { c2.x(), c2.y(), CurveToDataElement };
849 Element ee = { e.x(), e.y(), CurveToDataElement };
850 d->elements << ce1 << ce2 << ee;
854
855
856
857
858
859
860
861
864
865
866
867
868
869
870
871
872
873
874
875void QPainterPath::quadTo(
const QPointF &c,
const QPointF &e)
878 printf(
"QPainterPath::quadTo() (%.2f,%.2f), (%.2f,%.2f)\n",
879 c.x(), c.y(), e.x(), e.y());
882 if (!hasValidCoords(c) || !hasValidCoords(e)) {
884 qWarning(
"QPainterPath::quadTo: Adding point with invalid coordinates, ignoring call");
893 Q_ASSERT(!d->elements.isEmpty());
894 const QPainterPath::Element &elm = d->elements.at(elementCount()-1);
895 QPointF prev(elm.x, elm.y);
899 if (prev == c && c == e)
902 QPointF c1((prev.x() + 2*c.x()) / 3, (prev.y() + 2*c.y()) / 3);
903 QPointF c2((e.x() + 2*c.x()) / 3, (e.y() + 2*c.y()) / 3);
908
909
910
911
912
913
914
915
916
917
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950void QPainterPath::arcTo(
const QRectF &rect, qreal startAngle, qreal sweepLength)
953 printf(
"QPainterPath::arcTo() (%.2f, %.2f, %.2f, %.2f, angle=%.2f, sweep=%.2f\n",
954 rect.x(), rect.y(), rect.width(), rect.height(), startAngle, sweepLength);
957 if (!hasValidCoords(rect) || !isValidCoord(startAngle) || !isValidCoord(sweepLength)) {
959 qWarning(
"QPainterPath::arcTo: Adding point with invalid coordinates, ignoring call");
972 QPointF curve_start = qt_curves_for_arc(rect, startAngle, sweepLength, pts, &point_count);
975 for (
int i=0; i<point_count; i+=3) {
976 cubicTo(pts[i].x(), pts[i].y(),
977 pts[i+1].x(), pts[i+1].y(),
978 pts[i+2].x(), pts[i+2].y());
985
986
987
988
989
990
991
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1011void QPainterPath::arcMoveTo(
const QRectF &rect, qreal angle)
1013 if (!hasValidCoords(rect) || !isValidCoord(angle)) {
1015 qWarning(
"QPainterPath::arcMoveTo: Adding point with invalid coordinates, ignoring call");
1024 qt_find_ellipse_coords(rect, angle, 0, &pt,
nullptr);
1031
1032
1033
1034
1035QPointF QPainterPath::currentPosition()
const
1037 return !d_ptr || d_func()->elements.isEmpty()
1039 : QPointF(d_func()->elements.constLast().x, d_func()->elements.constLast().y);
1044
1045
1046
1047
1048
1049
1050
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072void QPainterPath::addRect(
const QRectF &r)
1074 if (!hasValidCoords(r)) {
1076 qWarning(
"QPainterPath::addRect: Adding point with invalid coordinates, ignoring call");
1087 bool first = d_func()->elements.size() < 2;
1089 moveTo(r.x(), r.y());
1091 Element l1 = { r.x() + r.width(), r.y(), LineToElement };
1092 Element l2 = { r.x() + r.width(), r.y() + r.height(), LineToElement };
1093 Element l3 = { r.x(), r.y() + r.height(), LineToElement };
1094 Element l4 = { r.x(), r.y(), LineToElement };
1096 d_func()->elements << l1 << l2 << l3 << l4;
1097 d_func()->require_moveTo =
true;
1098 d_func()->convex = first;
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119void QPainterPath::addPolygon(
const QPolygonF &polygon)
1121 if (polygon.isEmpty())
1127 moveTo(polygon.constFirst());
1128 for (
int i=1; i<polygon.size(); ++i) {
1129 Element elm = { polygon.at(i).x(), polygon.at(i).y(), LineToElement };
1130 d_func()->elements << elm;
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157void QPainterPath::addEllipse(
const QRectF &boundingRect)
1159 if (!hasValidCoords(boundingRect)) {
1161 qWarning(
"QPainterPath::addEllipse: Adding point with invalid coordinates, ignoring call");
1166 if (boundingRect.isNull())
1172 bool first = d_func()->elements.size() < 2;
1176 QPointF start = qt_curves_for_arc(boundingRect, 0, -360, pts, &point_count);
1179 cubicTo(pts[0], pts[1], pts[2]);
1180 cubicTo(pts[3], pts[4], pts[5]);
1181 cubicTo(pts[6], pts[7], pts[8]);
1182 cubicTo(pts[9], pts[10], pts[11]);
1183 d_func()->require_moveTo =
true;
1185 d_func()->convex = first;
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209void QPainterPath::addText(
const QPointF &point,
const QFont &f,
const QString &text)
1217 QTextLayout layout(text, f);
1218 layout.setCacheEnabled(
true);
1220 QTextOption opt = layout.textOption();
1221 opt.setUseDesignMetrics(
true);
1222 layout.setTextOption(opt);
1224 QTextEngine *eng = layout.engine();
1225 layout.beginLayout();
1226 QTextLine line = layout.createLine();
1229 const QScriptLine &sl = eng->lines[0];
1230 if (!sl.length || !eng->layoutData)
1233 int nItems = eng->layoutData->items.size();
1238 QVarLengthArray<
int> visualOrder(nItems);
1239 QVarLengthArray<uchar> levels(nItems);
1240 for (
int i = 0; i < nItems; ++i)
1241 levels[i] = eng->layoutData->items.at(i).analysis.bidiLevel;
1242 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
1244 for (
int i = 0; i < nItems; ++i) {
1245 int item = visualOrder[i];
1246 const QScriptItem &si = eng->layoutData->items.at(item);
1248 if (si.analysis.flags < QScriptAnalysis::TabOrObject) {
1249 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
1250 QFontEngine *fe = eng->fontEngine(si);
1252 fe->addOutlineToPath(x, y, glyphs,
this,
1253 si.analysis.bidiLevel % 2
1254 ? QTextItem::RenderFlags(QTextItem::RightToLeft)
1255 : QTextItem::RenderFlags{});
1257 const qreal lw = fe->lineThickness().toReal();
1258 if (f.d->underline) {
1259 qreal pos = fe->underlinePosition().toReal();
1260 addRect(x, y + pos, si.width.toReal(), lw);
1262 if (f.d->overline) {
1263 qreal pos = fe->ascent().toReal() + 1;
1264 addRect(x, y - pos, si.width.toReal(), lw);
1266 if (f.d->strikeOut) {
1267 qreal pos = fe->ascent().toReal() / 3;
1268 addRect(x, y - pos, si.width.toReal(), lw);
1271 x += si.width.toReal();
1276
1277
1278
1279
1280
1281
1282
1283void QPainterPath::addPath(
const QPainterPath &other)
1285 if (other.isEmpty())
1291 QPainterPathPrivate *d = d_func();
1293 if (d->elements.constLast().type == MoveToElement)
1294 d->elements.remove(d->elements.size()-1);
1297 int cStart = d->elements.size() + other.d_func()->cStart;
1298 d->elements += other.d_func()->elements;
1301 d->require_moveTo = other.d_func()->isClosed();
1306
1307
1308
1309
1310
1311
1312
1313
1314void QPainterPath::connectPath(
const QPainterPath &other)
1316 if (other.isEmpty())
1322 QPainterPathPrivate *d = d_func();
1324 if (d->elements.constLast().type == MoveToElement)
1325 d->elements.remove(d->elements.size()-1);
1328 int cStart = d->elements.size() + other.d_func()->cStart;
1329 int first = d->elements.size();
1330 d->elements += other.d_func()->elements;
1333 d->elements[first].type = LineToElement;
1336 if (first > 0 && QPointF(d->elements.at(first)) == QPointF(d->elements.at(first - 1))) {
1337 d->elements.remove(first--);
1341 if (cStart != first)
1346
1347
1348
1349
1350
1351
1352void QPainterPath::addRegion(
const QRegion ®ion)
1357 for (
const QRect &rect : region)
1363
1364
1365
1366
1367Qt::FillRule QPainterPath::fillRule()
const
1369 return d_func() && d_func()->hasWindingFill ? Qt::WindingFill : Qt::OddEvenFill;
1373
1374
1375
1376
1377
1378
1379
1380
1381
1382
1383
1384
1385
1386
1387
1388
1389void QPainterPath::setFillRule(Qt::FillRule fillRule)
1392 const bool isWindingRequested = (fillRule == Qt::WindingFill);
1393 if (d_func()->hasWindingFill == isWindingRequested)
1397 d_func()->hasWindingFill = isWindingRequested;
1400#define QT_BEZIER_A(bezier, coord) 3
* (-bezier.coord##1
1405#define QT_BEZIER_B(bezier, coord) 6
* (bezier.coord##1
1409#define QT_BEZIER_C(bezier, coord) 3
* (- bezier.coord##1
1412#define QT_BEZIER_CHECK_T(bezier, t)
1413 if (t >= 0
&& t <= 1
) {
1414 QPointF p(b.pointAt(t));
1415 if (p.x() < minx) minx = p.x();
1416 else if (p.x() > maxx) maxx = p.x();
1417 if (p.y() < miny) miny = p.y();
1418 else if (p.y() > maxy) maxy = p.y();
1424 qreal minx, miny, maxx, maxy;
1448 if (qFuzzyIsNull(ax)) {
1451 if (!qFuzzyIsNull(bx)) {
1457 const qreal tx = bx * bx - 4 * ax * cx;
1460 qreal temp = qSqrt(tx);
1461 qreal rcp = 1 / (2 * ax);
1462 qreal t1 = (-bx + temp) * rcp;
1465 qreal t2 = (-bx - temp) * rcp;
1478 if (qFuzzyIsNull(ay)) {
1481 if (!qFuzzyIsNull(by)) {
1487 const qreal ty = by * by - 4 * ay * cy;
1490 qreal temp = qSqrt(ty);
1491 qreal rcp = 1 / (2 * ay);
1492 qreal t1 = (-by + temp) * rcp;
1495 qreal t2 = (-by - temp) * rcp;
1500 return QRectF(minx, miny, maxx - minx, maxy - miny);
1504
1505
1506
1507
1508
1509QRectF QPainterPath::boundingRect()
const
1513 QPainterPathPrivate *d = d_func();
1516 computeBoundingRect();
1521
1522
1523
1524
1525
1526
1527
1528
1529
1530QRectF QPainterPath::controlPointRect()
const
1534 QPainterPathPrivate *d = d_func();
1536 if (d->dirtyControlBounds)
1537 computeControlPointRect();
1538 return d->controlBounds;
1543
1544
1545
1546
1547
1548
1549
1551bool QPainterPath::isEmpty()
const
1553 return !d_ptr || (d_ptr->elements.size() == 1 && d_ptr->elements.constFirst().type == MoveToElement);
1557
1558
1559
1560
1561
1562
1563
1564QPainterPath QPainterPath::toReversed()
const
1566 Q_D(
const QPainterPath);
1574 rev.moveTo(d->elements.at(d->elements.size()-1).x, d->elements.at(d->elements.size()-1).y);
1576 for (
int i=d->elements.size()-1; i>=1; --i) {
1577 const QPainterPath::Element &elm = d->elements.at(i);
1578 const QPainterPath::Element &prev = d->elements.at(i-1);
1581 rev.lineTo(prev.x, prev.y);
1584 rev.moveTo(prev.x, prev.y);
1586 case CurveToDataElement:
1589 const QPainterPath::Element &cp1 = d->elements.at(i-2);
1590 const QPainterPath::Element &sp = d->elements.at(i-3);
1591 Q_ASSERT(prev.type == CurveToDataElement);
1592 Q_ASSERT(cp1.type == CurveToElement);
1593 rev.cubicTo(prev.x, prev.y, cp1.x, cp1.y, sp.x, sp.y);
1598 Q_ASSERT(!
"qt_reversed_path");
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618QList<QPolygonF> QPainterPath::toSubpathPolygons(
const QTransform &matrix)
const
1621 Q_D(
const QPainterPath);
1622 QList<QPolygonF> flatCurves;
1627 for (
int i=0; i<elementCount(); ++i) {
1628 const QPainterPath::Element &e = d->elements.at(i);
1630 case QPainterPath::MoveToElement:
1631 if (current.size() > 1)
1632 flatCurves += current;
1634 current.reserve(16);
1635 current += QPointF(e.x, e.y) * matrix;
1637 case QPainterPath::LineToElement:
1638 current += QPointF(e.x, e.y) * matrix;
1640 case QPainterPath::CurveToElement: {
1641 Q_ASSERT(d->elements.at(i+1).type == QPainterPath::CurveToDataElement);
1642 Q_ASSERT(d->elements.at(i+2).type == QPainterPath::CurveToDataElement);
1643 QBezier bezier = QBezier::fromPoints(QPointF(d->elements.at(i-1).x, d->elements.at(i-1).y) * matrix,
1644 QPointF(e.x, e.y) * matrix,
1645 QPointF(d->elements.at(i+1).x, d->elements.at(i+1).y) * matrix,
1646 QPointF(d->elements.at(i+2).x, d->elements.at(i+2).y) * matrix);
1647 bezier.addToPolygon(¤t);
1651 case QPainterPath::CurveToDataElement:
1652 Q_ASSERT(!
"QPainterPath::toSubpathPolygons(), bad element type");
1657 if (current.size()>1)
1658 flatCurves += current;
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681
1682
1683
1684
1685QList<QPolygonF> QPainterPath::toFillPolygons(
const QTransform &matrix)
const
1688 QList<QPolygonF> polys;
1690 QList<QPolygonF> subpaths = toSubpathPolygons(matrix);
1691 int count = subpaths.size();
1696 QList<QRectF> bounds;
1697 bounds.reserve(count);
1698 for (
int i=0; i<count; ++i)
1699 bounds += subpaths.at(i).boundingRect();
1701#ifdef QPP_FILLPOLYGONS_DEBUG
1702 printf(
"QPainterPath::toFillPolygons, subpathCount=%d\n", count);
1703 for (
int i=0; i<bounds.size(); ++i)
1704 qDebug() <<
" bounds" << i << bounds.at(i);
1707 QList< QList<
int> > isects;
1708 isects.resize(count);
1711 for (
int j=0; j<count; ++j) {
1712 if (subpaths.at(j).size() <= 2)
1714 QRectF cbounds = bounds.at(j);
1715 for (
int i=0; i<count; ++i) {
1716 if (cbounds.intersects(bounds.at(i))) {
1722#ifdef QPP_FILLPOLYGONS_DEBUG
1723 printf(
"Intersections before flattening:\n");
1724 for (
int i = 0; i < count; ++i) {
1726 for (
int j = 0; j < isects[i].size(); ++j) {
1727 printf(
"%d ", isects[i][j]);
1734 for (
int i=0; i<count; ++i) {
1735 const QList<
int> ¤t_isects = isects.at(i);
1736 for (
int j=0; j<current_isects.size(); ++j) {
1737 int isect_j = current_isects.at(j);
1740 const QList<
int> &isects_j = isects.at(isect_j);
1741 for (
int k = 0, size = isects_j.size(); k < size; ++k) {
1742 int isect_k = isects_j.at(k);
1743 if (isect_k != i && !isects.at(i).contains(isect_k)) {
1744 isects[i] += isect_k;
1747 isects[isect_j].clear();
1751#ifdef QPP_FILLPOLYGONS_DEBUG
1752 printf(
"Intersections after flattening:\n");
1753 for (
int i = 0; i < count; ++i) {
1755 for (
int j = 0; j < isects[i].size(); ++j) {
1756 printf(
"%d ", isects[i][j]);
1763 for (
int i=0; i<count; ++i) {
1764 const QList<
int> &subpath_list = isects.at(i);
1765 if (!subpath_list.isEmpty()) {
1767 for (
int j=0; j<subpath_list.size(); ++j) {
1768 const QPolygonF &subpath = subpaths.at(subpath_list.at(j));
1770 if (!subpath.isClosed())
1771 buildUp += subpath.first();
1772 if (!buildUp.isClosed())
1773 buildUp += buildUp.constFirst();
1796 if (QtPrivate::fuzzyCompare(y1, y2)) {
1799 }
else if (y2 < y1) {
1800 qreal x_tmp = x2; x2 = x1; x1 = x_tmp;
1801 qreal y_tmp = y2; y2 = y1; y1 = y_tmp;
1805 if (y >= y1 && y < y2) {
1806 qreal x = x1 + ((x2 - x1) / (y2 - y1)) * (y - y1);
1816 int *winding,
int depth = 0)
1820 QRectF bounds = bezier.bounds();
1826 if (y >= bounds.y() && y < bounds.y() + bounds.height()) {
1830 const qreal lower_bound = qreal(.001);
1831 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound)) {
1835 if (bezier.pt1().x() <= x) {
1836 (*winding) += (bezier.pt4().y() > bezier.pt1().y() ? 1 : -1);
1842 const auto halves = bezier.split();
1843 qt_painterpath_isect_curve(halves.first, pt, winding, depth + 1);
1844 qt_painterpath_isect_curve(halves.second, pt, winding, depth + 1);
1849
1850
1851
1852
1853
1854
1855
1856bool QPainterPath::contains(
const QPointF &pt)
const
1858 if (isEmpty() || !controlPointRect().contains(pt))
1861 QPainterPathPrivate *d = d_func();
1863 int winding_number = 0;
1867 for (
int i=0; i<d->elements.size(); ++i) {
1868 const Element &e = d->elements.at(i);
1874 qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number);
1875 last_start = last_pt = e;
1879 qt_painterpath_isect_line(last_pt, e, pt, &winding_number);
1883 case CurveToElement:
1885 const QPainterPath::Element &cp2 = d->elements.at(++i);
1886 const QPainterPath::Element &ep = d->elements.at(++i);
1887 qt_painterpath_isect_curve(QBezier::fromPoints(last_pt, e, cp2, ep),
1888 pt, &winding_number);
1900 if (last_pt != last_start)
1901 qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number);
1903 return (d->hasWindingFill
1904 ? (winding_number != 0)
1905 : ((winding_number % 2) != 0));
1913 qreal left = rect.left();
1914 qreal right = rect.right();
1915 qreal top = rect.top();
1916 qreal bottom = rect.bottom();
1919 int p1 = ((x1 < left) <<
Left)
1920 | ((x1 > right) <<
Right)
1921 | ((y1 < top) <<
Top)
1922 | ((y1 > bottom) <<
Bottom);
1923 int p2 = ((x2 < left) <<
Left)
1924 | ((x2 > right) <<
Right)
1925 | ((y2 < top) <<
Top)
1926 | ((y2 > bottom) <<
Bottom);
1938 y1 += dy/dx * (left - x1);
1940 }
else if (x1 > right) {
1941 y1 -= dy/dx * (x1 - right);
1945 y2 += dy/dx * (left - x2);
1947 }
else if (x2 > right) {
1948 y2 -= dy/dx * (x2 - right);
1952 p1 = ((y1 < top) <<
Top)
1953 | ((y1 > bottom) <<
Bottom);
1954 p2 = ((y2 < top) <<
Top)
1955 | ((y2 > bottom) <<
Bottom);
1962 x1 += dx/dy * (top - y1);
1964 }
else if (y1 > bottom) {
1965 x1 -= dx/dy * (y1 - bottom);
1969 x2 += dx/dy * (top - y2);
1971 }
else if (y2 > bottom) {
1972 x2 -= dx/dy * (y2 - bottom);
1976 p1 = ((x1 < left) <<
Left)
1977 | ((x1 > right) <<
Right);
1978 p2 = ((x2 < left) <<
Left)
1979 | ((x2 > right) <<
Right);
1991 QRectF bounds = bezier.bounds();
1993 if (y >= bounds.top() && y < bounds.bottom()
1994 && bounds.right() >= x1 && bounds.left() < x2) {
1995 const qreal lower_bound = qreal(.01);
1996 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound))
1999 const auto halves = bezier.split();
2000 if (qt_isect_curve_horizontal(halves.first, y, x1, x2, depth + 1)
2001 || qt_isect_curve_horizontal(halves.second, y, x1, x2, depth + 1))
2009 QRectF bounds = bezier.bounds();
2011 if (x >= bounds.left() && x < bounds.right()
2012 && bounds.bottom() >= y1 && bounds.top() < y2) {
2013 const qreal lower_bound = qreal(.01);
2014 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound))
2017 const auto halves = bezier.split();
2018 if (qt_isect_curve_vertical(halves.first, x, y1, y2, depth + 1)
2019 || qt_isect_curve_vertical(halves.second, x, y1, y2, depth + 1))
2027 if ((point.x() == rect.left() || point.x() == rect.right()) &&
2028 (point.y() >= rect.top() && point.y() <= rect.bottom()))
2030 if ((point.y() == rect.top() || point.y() == rect.bottom()) &&
2031 (point.x() >= rect.left() && point.x() <= rect.right()))
2037
2038
2043 enum { OnRect, InsideRect, OutsideRect} edgeStatus = OnRect;
2044 for (
int i=0; i<path->elementCount(); ++i) {
2045 const QPainterPath::Element &e = path->elementAt(i);
2049 case QPainterPath::MoveToElement:
2051 && qFuzzyCompare(last_pt, last_start)
2052 && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
2053 last_start.x(), last_start.y(), rect))
2055 last_start = last_pt = e;
2058 case QPainterPath::LineToElement:
2059 if (qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(), e.x, e.y, rect))
2064 case QPainterPath::CurveToElement:
2066 QPointF cp2 = path->elementAt(++i);
2067 QPointF ep = path->elementAt(++i);
2068 QBezier bezier = QBezier::fromPoints(last_pt, e, cp2, ep);
2069 if (qt_isect_curve_horizontal(bezier, rect.top(), rect.left(), rect.right())
2070 || qt_isect_curve_horizontal(bezier, rect.bottom(), rect.left(), rect.right())
2071 || qt_isect_curve_vertical(bezier, rect.left(), rect.top(), rect.bottom())
2072 || qt_isect_curve_vertical(bezier, rect.right(), rect.top(), rect.bottom()))
2083 if (!pointOnEdge(rect, last_pt)) {
2084 bool contained = rect.contains(last_pt);
2085 switch (edgeStatus) {
2095 edgeStatus = contained ? InsideRect : OutsideRect;
2099 if (last_pt == last_start)
2100 edgeStatus = OnRect;
2105 if (last_pt != last_start
2106 && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
2107 last_start.x(), last_start.y(), rect))
2114
2115
2116
2117
2118
2119
2120
2121
2122
2123
2124
2125
2126
2127bool QPainterPath::intersects(
const QRectF &rect)
const
2129 if (elementCount() == 1 && rect.contains(elementAt(0)))
2135 QRectF cp = controlPointRect();
2136 QRectF rn = rect.normalized();
2141 if (qMax(rn.left(), cp.left()) > qMin(rn.right(), cp.right())
2142 || qMax(rn.top(), cp.top()) > qMin(rn.bottom(), cp.bottom()))
2146 if (qt_painterpath_check_crossing(
this, rect))
2149 if (contains(rect.center()))
2155 for (
int i=0; i<d->elements.size(); ++i) {
2156 const Element &e = d->elements.at(i);
2157 if (e.type == QPainterPath::MoveToElement && rect.contains(e))
2165
2166
2167
2168
2169
2170void QPainterPath::translate(qreal dx, qreal dy)
2172 if (!d_ptr || (dx == 0 && dy == 0))
2175 int elementsLeft = d_ptr->elements.size();
2176 if (elementsLeft <= 0)
2180 QPainterPath::Element *element = d_func()->elements.data();
2182 while (elementsLeft--) {
2190
2191
2192
2193
2194
2195
2196
2197
2200
2201
2202
2203
2204
2205QPainterPath QPainterPath::translated(qreal dx, qreal dy)
const
2207 QPainterPath copy(*
this);
2208 copy.translate(dx, dy);
2213
2214
2215
2216
2217
2218
2219
2220
2223
2224
2225
2226
2227
2228bool QPainterPath::contains(
const QRectF &rect)
const
2234 if (isEmpty() || !controlPointRect().contains(rect))
2240 if (qt_painterpath_check_crossing(
this, rect)) {
2241 if (fillRule() == Qt::OddEvenFill) {
2246 if (!contains(rect.topLeft()) ||
2247 !contains(rect.topRight()) ||
2248 !contains(rect.bottomRight()) ||
2249 !contains(rect.bottomLeft()))
2258 if (!contains(rect.center()))
2267 for (
int i=0; i<d->elements.size(); ++i) {
2268 const Element &e = d->elements.at(i);
2269 if (e.type == QPainterPath::MoveToElement && rect.contains(e)) {
2270 if (fillRule() == Qt::OddEvenFill)
2274 for (; !stop && i<d->elements.size(); ++i) {
2275 const Element &el = d->elements.at(i);
2284 case CurveToElement:
2285 if (!contains(d->elements.at(i+2)))
2302static inline bool epsilonCompare(
const QPointF &a,
const QPointF &b,
const QSizeF &epsilon)
2304 return qAbs(a.x() - b.x()) <= epsilon.width()
2305 && qAbs(a.y() - b.y()) <= epsilon.height();
2309
2310
2311
2312
2313
2314
2315
2317bool QPainterPath::operator==(
const QPainterPath &path)
const
2319 QPainterPathPrivate *d = d_func();
2320 QPainterPathPrivate *other_d = path.d_func();
2323 }
else if (!d || !other_d) {
2324 if (!other_d && isEmpty() && elementAt(0) == QPointF() && !d->hasWindingFill)
2326 if (!d && path.isEmpty() && path.elementAt(0) == QPointF() && !other_d->hasWindingFill)
2330 else if (d->hasWindingFill != other_d->hasWindingFill)
2332 else if (d->elements.size() != other_d->elements.size())
2335 const qreal qt_epsilon =
sizeof(qreal) ==
sizeof(
double) ? 1e-12 : qreal(1e-5);
2337 QSizeF epsilon = boundingRect().size();
2338 epsilon.rwidth() *= qt_epsilon;
2339 epsilon.rheight() *= qt_epsilon;
2341 for (
int i = 0; i < d->elements.size(); ++i)
2342 if (d->elements.at(i).type != other_d->elements.at(i).type
2343 || !epsilonCompare(d->elements.at(i), other_d->elements.at(i), epsilon))
2350
2351
2352
2353
2354
2355
2356
2358bool QPainterPath::operator!=(
const QPainterPath &path)
const
2360 return !(*
this==path);
2364
2365
2366
2367
2368
2369
2370QPainterPath QPainterPath::operator&(
const QPainterPath &other)
const
2372 return intersected(other);
2376
2377
2378
2379
2380
2381
2382QPainterPath QPainterPath::operator|(
const QPainterPath &other)
const
2384 return united(other);
2388
2389
2390
2391
2392
2393
2394
2395QPainterPath QPainterPath::operator+(
const QPainterPath &other)
const
2397 return united(other);
2401
2402
2403
2404
2405
2406
2407QPainterPath QPainterPath::operator-(
const QPainterPath &other)
const
2409 return subtracted(other);
2413
2414
2415
2416
2417
2418
2419QPainterPath &QPainterPath::operator&=(
const QPainterPath &other)
2421 return *
this = (*
this & other);
2425
2426
2427
2428
2429
2430
2431QPainterPath &QPainterPath::operator|=(
const QPainterPath &other)
2433 return *
this = (*
this | other);
2437
2438
2439
2440
2441
2442
2443
2444QPainterPath &QPainterPath::operator+=(
const QPainterPath &other)
2446 return *
this = (*
this + other);
2450
2451
2452
2453
2454
2455
2456
2457QPainterPath &QPainterPath::operator-=(
const QPainterPath &other)
2459 return *
this = (*
this - other);
2462#ifndef QT_NO_DATASTREAM
2464
2465
2466
2467
2468
2469
2470
2471
2479 s << p.elementCount();
2480 for (
int i=0; i < p.d_func()->elements.size(); ++i) {
2481 const QPainterPath::Element &e = p.d_func()->elements.at(i);
2483 s <<
double(e.x) <<
double(e.y);
2485 s << p.d_func()->cStart;
2486 s <<
int(p.fillRule());
2491
2492
2493
2494
2495
2496
2497
2498
2499
2500
2501
2502
2503
2504
2505
2508 bool errorDetected =
false;
2518 s.setStatus(QDataStream::ReadCorruptData);
2524 QList<QPainterPath::Element> &elements = p.d_func()->elements;
2526 bool folded =
false;
2527 int curveDataPending = 0;
2528 for (
int i=0; i<size; ++i) {
2534 if (type < QPainterPath::MoveToElement || type > QPainterPath::CurveToDataElement) {
2536 qWarning(
"QDataStream::operator>>: Invalid QPainterPath element type read, skipping it");
2538 errorDetected =
true;
2541 if (!isValidCoord(qreal(x)) || !isValidCoord(qreal(y))) {
2543 qWarning(
"QDataStream::operator>>: Invalid QPainterPath coordinates read, skipping it");
2545 errorDetected =
true;
2548 QPainterPath::Element elm = { qreal(x), qreal(y), QPainterPath::ElementType(type) };
2552 if (curveDataPending > 0) {
2553 if (elm.type != QPainterPath::CurveToDataElement) {
2554 errorDetected =
true;
2558 }
else if (elm.type == QPainterPath::CurveToDataElement
2559 || (elements.isEmpty() && elm.type != QPainterPath::MoveToElement)) {
2560 errorDetected =
true;
2562 }
else if (elm.type == QPainterPath::CurveToElement) {
2563 curveDataPending = 2;
2564 }
else if (elm.type == QPainterPath::MoveToElement && !elements.isEmpty()
2565 && elements.last().type == QPainterPath::MoveToElement) {
2566 elements.last() = elm;
2570 elements.append(elm);
2572 if (curveDataPending > 0)
2573 errorDetected =
true;
2578 if (fillRule != Qt::OddEvenFill && fillRule != Qt::WindingFill)
2579 errorDetected =
true;
2580 if (errorDetected || elements.isEmpty()) {
2582 qWarning(
"QDataStream::operator>>: Invalid QPainterPath read, resetting it");
2585 s.setStatus(QDataStream::ReadCorruptData);
2588 p.d_func()->hasWindingFill = (Qt::FillRule(fillRule) == Qt::WindingFill);
2592 if (folded || cStart < 0 || cStart >= elements.size()
2593 || elements.at(cStart).type != QPainterPath::MoveToElement) {
2594 cStart = elements.size() - 1;
2595 while (elements.at(cStart).type != QPainterPath::MoveToElement)
2598 p.d_func()->cStart = cStart;
2605
2606
2610 ((QPainterPath *) data)->moveTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
2615 ((QPainterPath *) data)->lineTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
2619 qfixed c2x, qfixed c2y,
2620 qfixed ex, qfixed ey,
2623 ((QPainterPath *) data)->cubicTo(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y),
2624 qt_fixed_to_real(c2x), qt_fixed_to_real(c2y),
2625 qt_fixed_to_real(ex), qt_fixed_to_real(ey));
2629
2630
2631
2632
2633
2634
2635
2636
2637
2638
2639
2640
2641
2642
2643
2644
2645
2646
2647
2648
2649
2650
2651
2652
2653
2654
2655
2656
2657
2658
2659
2660
2661
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674
2675
2676
2677
2682 stroker.setMoveToHook(qt_path_stroke_move_to);
2683 stroker.setLineToHook(qt_path_stroke_line_to);
2684 stroker.setCubicToHook(qt_path_stroke_cubic_to);
2688
2689
2690QPainterPathStroker::QPainterPathStroker()
2691 : d_ptr(
new QPainterPathStrokerPrivate)
2696
2697
2698
2699
2700QPainterPathStroker::QPainterPathStroker(
const QPen &pen)
2701 : d_ptr(
new QPainterPathStrokerPrivate)
2703 setWidth(pen.widthF());
2704 setCapStyle(pen.capStyle());
2705 setJoinStyle(pen.joinStyle());
2706 setMiterLimit(pen.miterLimit());
2707 setDashOffset(pen.dashOffset());
2709 if (pen.style() == Qt::CustomDashLine)
2710 setDashPattern(pen.dashPattern());
2712 setDashPattern(pen.style());
2716
2717
2718QPainterPathStroker::~QPainterPathStroker()
2724
2725
2726
2727
2728
2729
2730
2731
2732
2733
2734
2735
2736QPainterPath QPainterPathStroker::createStroke(
const QPainterPath &path)
const
2738 QPainterPathStrokerPrivate *d =
const_cast<QPainterPathStrokerPrivate *>(d_func());
2739 QPainterPath stroke;
2742 if (d->dashPattern.isEmpty()) {
2743 d->stroker.strokePath(path, &stroke, QTransform());
2745 QDashStroker dashStroker(&d->stroker);
2746 dashStroker.setDashPattern(d->dashPattern);
2747 dashStroker.setDashOffset(d->dashOffset);
2748 dashStroker.setClipRect(d->stroker.clipRect());
2749 if (d->stroker.curveThresholdExplicitlySet())
2750 dashStroker.setCurveThreshold(d->stroker.curveThreshold());
2751 dashStroker.strokePath(path, &stroke, QTransform());
2753 stroke.setFillRule(Qt::WindingFill);
2758
2759
2760
2761
2762
2763void QPainterPathStroker::setWidth(qreal width)
2765 Q_D(QPainterPathStroker);
2768 d->stroker.setStrokeWidth(qt_real_to_fixed(width));
2772
2773
2774qreal QPainterPathStroker::width()
const
2776 return qt_fixed_to_real(d_func()->stroker.strokeWidth());
2781
2782
2783
2784
2785void QPainterPathStroker::setCapStyle(Qt::PenCapStyle style)
2787 d_func()->stroker.setCapStyle(style);
2792
2793
2794Qt::PenCapStyle QPainterPathStroker::capStyle()
const
2796 return d_func()->stroker.capStyle();
2800
2801
2802void QPainterPathStroker::setJoinStyle(Qt::PenJoinStyle style)
2804 d_func()->stroker.setJoinStyle(style);
2808
2809
2810Qt::PenJoinStyle QPainterPathStroker::joinStyle()
const
2812 return d_func()->stroker.joinStyle();
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825void QPainterPathStroker::setMiterLimit(qreal limit)
2827 d_func()->stroker.setMiterLimit(qt_real_to_fixed(limit));
2831
2832
2833qreal QPainterPathStroker::miterLimit()
const
2835 return qt_fixed_to_real(d_func()->stroker.miterLimit());
2840
2841
2842
2843
2844
2845
2846
2847void QPainterPathStroker::setCurveThreshold(qreal threshold)
2849 d_func()->stroker.setCurveThreshold(qt_real_to_fixed(threshold));
2853
2854
2855
2856qreal QPainterPathStroker::curveThreshold()
const
2858 return qt_fixed_to_real(d_func()->stroker.curveThreshold());
2862
2863
2864void QPainterPathStroker::setDashPattern(Qt::PenStyle style)
2866 d_func()->dashPattern = QDashStroker::patternForStyle(style);
2870
2871
2872
2873
2874
2875
2876
2877
2878
2879
2880
2881
2882
2883
2884
2885void QPainterPathStroker::setDashPattern(
const QList<qreal> &dashPattern)
2887 d_func()->dashPattern.clear();
2888 for (
int i=0; i<dashPattern.size(); ++i)
2889 d_func()->dashPattern << qt_real_to_fixed(dashPattern.at(i));
2893
2894
2895QList<qreal> QPainterPathStroker::dashPattern()
const
2897 return d_func()->dashPattern;
2901
2902
2903qreal QPainterPathStroker::dashOffset()
const
2905 return d_func()->dashOffset;
2909
2910
2911
2912
2913
2914void QPainterPathStroker::setDashOffset(qreal offset)
2916 d_func()->dashOffset = offset;
2920
2921
2922
2923
2924
2925
2926
2927
2928
2929
2930
2931
2932
2933
2934QPolygonF QPainterPath::toFillPolygon(
const QTransform &matrix)
const
2936 const QList<QPolygonF> flats = toSubpathPolygons(matrix);
2938 if (flats.isEmpty())
2940 QPointF first = flats.first().first();
2941 for (
int i=0; i<flats.size(); ++i) {
2942 polygon += flats.at(i);
2943 if (!flats.at(i).isClosed())
2944 polygon += flats.at(i).first();
2952
2953
2954
2955
2956
2957bool QPainterPath::isCachingEnabled()
const
2960 return d && d->cacheEnabled;
2964
2965
2966
2967
2968
2969
2970
2971
2972
2973
2974
2975void QPainterPath::setCachingEnabled(
bool enabled)
2978 if (d_func()->cacheEnabled == enabled)
2981 QPainterPathPrivate *d = d_func();
2982 d->cacheEnabled = enabled;
2984 d->m_runLengths.clear();
2985 d->m_runLengths.squeeze();
2992 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
2996
2997
2998qreal QPainterPath::length()
const
3003 if (d->cacheEnabled) {
3004 if (d->dirtyRunLengths)
3005 d->computeRunLengths();
3006 return d->m_runLengths.last();
3010 for (
int i=1; i<d->elements.size(); ++i) {
3011 const Element &e = d->elements.at(i);
3018 len += QLineF(d->elements.at(i-1), e).length();
3021 case CurveToElement:
3023 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
3025 d->elements.at(i+1),
3026 d->elements.at(i+2));
3039
3040
3041
3042
3043
3044
3045
3046qreal QPainterPath::percentAtLength(qreal len)
const
3049 if (isEmpty() || len <= 0)
3052 qreal totalLength = length();
3053 if (len > totalLength)
3056 Q_ASSERT(totalLength != 0);
3058 if (d->cacheEnabled) {
3059 const int ei = qMax(d->elementAtT(len / totalLength), 1);
3061 const QPainterPath::Element &e = d->elements[ei];
3063 case QPainterPath::LineToElement:
3064 res = len / totalLength;
3066 case CurveToElement:
3068 QBezier b = QBezier::fromPoints(d->elements.at(ei-1),
3070 d->elements.at(ei+1),
3071 d->elements.at(ei+2));
3072 qreal prevLen = d->m_runLengths[ei - 1];
3073 qreal blen = d->m_runLengths[ei] - prevLen;
3074 qreal elemRes = b.tAtLength(len - prevLen);
3075 res = (elemRes * blen + prevLen) / totalLength;
3085 for (
int i=1; i<d->elements.size(); ++i) {
3086 const Element &e = d->elements.at(i);
3093 QLineF line(d->elements.at(i-1), e);
3094 qreal llen = line.length();
3096 if (curLen >= len) {
3097 return len/totalLength ;
3102 case CurveToElement:
3104 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
3106 d->elements.at(i+1),
3107 d->elements.at(i+2));
3108 qreal blen = b.length();
3109 qreal prevLen = curLen;
3112 if (curLen >= len) {
3113 qreal res = b.tAtLength(len - prevLen);
3114 return (res * blen + prevLen)/totalLength;
3129 qreal *bezierLength)
3131 *startingLength = 0;
3136 qreal totalLength = path.length();
3138 const int lastElement = path.elementCount() - 1;
3139 for (
int i=0; i <= lastElement; ++i) {
3140 const QPainterPath::Element &e = path.elementAt(i);
3143 case QPainterPath::MoveToElement:
3145 case QPainterPath::LineToElement:
3147 QLineF line(path.elementAt(i-1), e);
3148 qreal llen = line.length();
3150 if (i == lastElement || curLen/totalLength >= t) {
3151 *bezierLength = llen;
3152 QPointF a = path.elementAt(i-1);
3153 QPointF delta = e - a;
3154 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
3158 case QPainterPath::CurveToElement:
3160 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
3162 path.elementAt(i+1),
3163 path.elementAt(i+2));
3164 qreal blen = b.length();
3167 if (i + 2 == lastElement || curLen/totalLength >= t) {
3168 *bezierLength = blen;
3178 *startingLength = curLen;
3184 qreal *bezierLength)
const
3186 Q_ASSERT(t >= 0 && t <= 1);
3188 if (!path.isEmpty() && d->cacheEnabled) {
3189 const int ei = qMax(d->elementAtT(t), 1);
3190 const qreal prevRunLength = d->m_runLengths[ei - 1];
3191 *startingLength = prevRunLength;
3192 *bezierLength = d->m_runLengths[ei] - prevRunLength;
3193 const QPointF prev = d->elements[ei - 1];
3194 const QPainterPath::Element &e = d->elements[ei];
3196 case QPainterPath::LineToElement:
3198 QPointF delta = (e - prev) / 3;
3199 return QBezier::fromPoints(prev, prev + delta, prev + 2 * delta, e);
3201 case QPainterPath::CurveToElement:
3202 return QBezier::fromPoints(prev, e, elements[ei + 1], elements[ei + 2]);
3209 return uncached_bezierAtT(path, t, startingLength, bezierLength);
3213
3214
3215
3216
3217
3218
3219
3220
3221QPointF QPainterPath::pointAtPercent(qreal t)
const
3223 if (t < 0 || t > 1) {
3224 qWarning(
"QPainterPath::pointAtPercent accepts only values between 0 and 1");
3228 if (!d_ptr || d_ptr->elements.size() == 0)
3231 if (d_ptr->elements.size() == 1)
3232 return d_ptr->elements.at(0);
3234 qreal totalLength = length();
3236 qreal bezierLen = 0;
3237 QBezier b = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3238 Q_ASSERT(bezierLen != 0);
3239 qreal realT = (totalLength * t - curLen) / bezierLen;
3241 return b.pointAt(qBound(qreal(0), realT, qreal(1)));
3245
3246
3247
3248
3249
3250
3251
3252
3253
3254
3255
3256qreal QPainterPath::angleAtPercent(qreal t)
const
3258 if (t < 0 || t > 1) {
3259 qWarning(
"QPainterPath::angleAtPercent accepts only values between 0 and 1");
3266 qreal totalLength = length();
3268 qreal bezierLen = 0;
3269 QBezier bez = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3270 Q_ASSERT(bezierLen != 0);
3271 qreal realT = (totalLength * t - curLen) / bezierLen;
3273 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3274 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3276 return QLineF(0, 0, m1, m2).angle();
3281
3282
3283
3284
3285
3286
3287
3288
3289qreal QPainterPath::slopeAtPercent(qreal t)
const
3291 if (t < 0 || t > 1) {
3292 qWarning(
"QPainterPath::slopeAtPercent accepts only values between 0 and 1");
3299 qreal totalLength = length();
3301 qreal bezierLen = 0;
3302 QBezier bez = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3303 Q_ASSERT(bezierLen != 0);
3304 qreal realT = (totalLength * t - curLen) / bezierLen;
3306 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3307 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3314 if (std::numeric_limits<qreal>::has_infinity) {
3315 slope = (m2 < 0) ? -std::numeric_limits<qreal>::infinity()
3316 : std::numeric_limits<qreal>::infinity();
3318 if (
sizeof(qreal) ==
sizeof(
double)) {
3319 return 1.79769313486231570e+308;
3321 return ((qreal)3.40282346638528860e+38);
3330
3331
3332
3333
3334
3335
3336
3337
3338
3339
3340
3341
3342
3343
3344
3346QPainterPath QPainterPath::trimmed(qreal fromFraction, qreal toFraction, qreal offset)
const
3352 if (!isCachingEnabled()) {
3353 QPainterPath copy(*
this);
3354 copy.setCachingEnabled(
true);
3355 return copy.trimmed(fromFraction, toFraction, offset);
3358 qreal f1 = qBound(qreal(0), fromFraction, qreal(1));
3359 qreal f2 = qBound(qreal(0), toFraction, qreal(1));
3360 if (qFuzzyIsNull(f1 - f2))
3361 return QPainterPath();
3364 if (qFuzzyCompare(f2 - f1, qreal(1)))
3368 res.setFillRule(fillRule());
3372 offset = std::modf(offset, &dummy);
3374 qreal of1 = f1 + offset;
3375 qreal of2 = f2 + offset;
3377 f1 = of1 < 0 ? of1 + 1 : of1;
3378 f2 = of2 + 1 > 1 ? of2 : of2 + 1;
3379 }
else if (offset > 0) {
3380 f1 = of1 - 1 < 0 ? of1 : of1 - 1;
3381 f2 = of2 > 1 ? of2 - 1 : of2;
3384 const bool wrapping = (f1 > f2);
3387 QPainterPathPrivate *d = d_func();
3388 if (d->dirtyRunLengths)
3389 d->computeRunLengths();
3390 const qreal totalLength = d->m_runLengths.last();
3391 if (qFuzzyIsNull(totalLength))
3394 const qreal l1 = f1 * totalLength;
3395 const qreal l2 = f2 * totalLength;
3396 const int e1 = d->elementAtLength(l1);
3397 const bool mustTrimE1 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e1), l1);
3398 const int e2 = d->elementAtLength(l2);
3399 const bool mustTrimE2 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e2), l2);
3402 if (e1 == e2 && !wrapping && mustTrimE1 && mustTrimE2) {
3404 d->appendSliceOfElement(&res, e1, l1, l2);
3408 d->appendEndOfElement(&res, e1, l1);
3410 res.moveTo(d->endPointOfElement(e1));
3413 int firstWholeElement = e1 + 1;
3414 int lastWholeElement = (mustTrimE2 ? e2 - 1 : e2);
3416 d->appendElementRange(&res, firstWholeElement, lastWholeElement);
3418 int lastIndex = d->elements.size() - 1;
3419 d->appendElementRange(&res, firstWholeElement, lastIndex);
3420 bool isClosed = (QPointF(d->elements.at(0)) == QPointF(d->elements.at(lastIndex)));
3422 d->appendElementRange(&res, (isClosed ? 1 : 0), lastWholeElement);
3427 d->appendStartOfElement(&res, e2, l2);
3434 qreal startLen, qreal endLen)
3436 Q_ASSERT(cacheEnabled);
3437 Q_ASSERT(!dirtyRunLengths);
3439 if (elemIdx <= 0 || elemIdx >= elements.size())
3442 const qreal prevLen = m_runLengths.at(elemIdx - 1);
3443 const qreal elemLen = m_runLengths.at(elemIdx) - prevLen;
3444 const qreal len1 = startLen - prevLen;
3445 const qreal len2 = endLen - prevLen;
3446 if (qFuzzyIsNull(elemLen))
3449 const QPointF pp = elements.at(elemIdx - 1);
3450 const QPainterPath::Element e = elements.at(elemIdx);
3453 QPointF p1 = (trimFlags & TrimStart) ? l.pointAt(len1 / elemLen) : pp;
3454 QPointF p2 = (trimFlags & TrimEnd) ? l.pointAt(len2 / elemLen) : e;
3458 }
else if (e.isCurveTo()) {
3459 Q_ASSERT(elemIdx < elements.size() - 2);
3460 QBezier b = QBezier::fromPoints(pp, e, elements.at(elemIdx + 1), elements.at(elemIdx + 2));
3461 qreal t1 = (trimFlags & TrimStart) ? b.tAtLength(len1) : 0.0;
3462 qreal t2 = (trimFlags & TrimEnd) ? b.tAtLength(len2) : 1.0;
3463 QBezier c = b.getSubRange(t1, t2);
3465 to->moveTo(c.pt1());
3466 to->cubicTo(c.pt2(), c.pt3(), c.pt4());
3474 if (first < 0 || first >= elements.size() || last < 0 || last >= elements.size())
3478 for (
int i = first; i <= last; i++) {
3479 const QPainterPath::Element &e = elements.at(i);
3481 case QPainterPath::MoveToElement:
3484 case QPainterPath::LineToElement:
3487 case QPainterPath::CurveToElement:
3488 Q_ASSERT(i < elements.size() - 2);
3489 to->cubicTo(e, elements.at(i + 1), elements.at(i + 2));
3501
3502
3503
3504
3505
3506
3507
3508
3509
3510
3511
3512
3513void QPainterPath::addRoundedRect(
const QRectF &rect, qreal xRadius, qreal yRadius,
3516 QRectF r = rect.normalized();
3521 if (mode == Qt::AbsoluteSize) {
3522 qreal w = r.width() / 2;
3523 qreal h = r.height() / 2;
3528 xRadius = 100 * qMin(xRadius, w) / w;
3533 yRadius = 100 * qMin(yRadius, h) / h;
3543 if (xRadius <= 0 || yRadius <= 0) {
3550 qreal w = r.width();
3551 qreal h = r.height();
3552 qreal rxx2 = w*xRadius/100;
3553 qreal ryy2 = h*yRadius/100;
3558 bool first = d_func()->elements.size() < 2;
3560 arcMoveTo(x, y, rxx2, ryy2, 180);
3561 arcTo(x, y, rxx2, ryy2, 180, -90);
3562 arcTo(x+w-rxx2, y, rxx2, ryy2, 90, -90);
3563 arcTo(x+w-rxx2, y+h-ryy2, rxx2, ryy2, 0, -90);
3564 arcTo(x, y+h-ryy2, rxx2, ryy2, 270, -90);
3567 d_func()->require_moveTo =
true;
3568 d_func()->convex = first;
3572
3573
3574
3575
3576
3577
3580
3581
3582
3583
3584
3585
3586
3587
3588
3589
3590
3591QPainterPath QPainterPath::united(
const QPainterPath &p)
const
3593 if (isEmpty() || p.isEmpty())
3594 return isEmpty() ? p : *
this;
3595 QPathClipper clipper(*
this, p);
3596 return clipper.clip(QPathClipper::BoolOr);
3600
3601
3602
3603
3604
3605
3606QPainterPath QPainterPath::intersected(
const QPainterPath &p)
const
3608 if (isEmpty() || p.isEmpty())
3609 return QPainterPath();
3610 QPathClipper clipper(*
this, p);
3611 return clipper.clip(QPathClipper::BoolAnd);
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624QPainterPath QPainterPath::subtracted(
const QPainterPath &p)
const
3626 if (isEmpty() || p.isEmpty())
3628 QPathClipper clipper(*
this, p);
3629 return clipper.clip(QPathClipper::BoolSub);
3633
3634
3635
3636
3637
3638
3639
3640
3641QPainterPath QPainterPath::simplified()
const
3645 QPathClipper clipper(*
this, QPainterPath());
3646 return clipper.clip(QPathClipper::Simplify);
3650
3651
3652
3653
3654
3655
3656
3657
3658
3659
3660bool QPainterPath::intersects(
const QPainterPath &p)
const
3662 if (p.elementCount() == 1)
3663 return contains(p.elementAt(0));
3664 if (isEmpty() || p.isEmpty())
3666 QPathClipper clipper(*
this, p);
3667 return clipper.intersect();
3671
3672
3673
3674
3675
3676
3677
3678
3679
3680
3681
3682bool QPainterPath::contains(
const QPainterPath &p)
const
3684 if (p.elementCount() == 1)
3685 return contains(p.elementAt(0));
3686 if (isEmpty() || p.isEmpty())
3688 QPathClipper clipper(*
this, p);
3689 return clipper.contains();
3692void QPainterPath::setDirty(
bool dirty)
3694 d_func()->pathConverter.reset();
3695 d_func()->dirtyBounds = dirty;
3696 d_func()->dirtyControlBounds = dirty;
3697 d_func()->dirtyRunLengths = dirty;
3698 d_func()->convex =
false;
3701void QPainterPath::computeBoundingRect()
const
3703 QPainterPathPrivate *d = d_func();
3704 d->dirtyBounds =
false;
3706 d->bounds = QRect();
3710 qreal minx, maxx, miny, maxy;
3711 minx = maxx = d->elements.at(0).x;
3712 miny = maxy = d->elements.at(0).y;
3713 for (
int i=1; i<d->elements.size(); ++i) {
3714 const Element &e = d->elements.at(i);
3719 if (e.x > maxx) maxx = e.x;
3720 else if (e.x < minx) minx = e.x;
3721 if (e.y > maxy) maxy = e.y;
3722 else if (e.y < miny) miny = e.y;
3724 case CurveToElement:
3726 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
3728 d->elements.at(i+1),
3729 d->elements.at(i+2));
3730 QRectF r = qt_painterpath_bezier_extrema(b);
3731 qreal right = r.right();
3732 qreal bottom = r.bottom();
3733 if (r.x() < minx) minx = r.x();
3734 if (right > maxx) maxx = right;
3735 if (r.y() < miny) miny = r.y();
3736 if (bottom > maxy) maxy = bottom;
3744 d->bounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3748void QPainterPath::computeControlPointRect()
const
3750 QPainterPathPrivate *d = d_func();
3751 d->dirtyControlBounds =
false;
3753 d->controlBounds = QRect();
3757 qreal minx, maxx, miny, maxy;
3758 minx = maxx = d->elements.at(0).x;
3759 miny = maxy = d->elements.at(0).y;
3760 for (
int i=1; i<d->elements.size(); ++i) {
3761 const Element &e = d->elements.at(i);
3762 if (e.x > maxx) maxx = e.x;
3763 else if (e.x < minx) minx = e.x;
3764 if (e.y > maxy) maxy = e.y;
3765 else if (e.y < miny) miny = e.y;
3767 d->controlBounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3772 Q_ASSERT(!elements.isEmpty());
3774 m_runLengths.clear();
3775 const int numElems = elements.size();
3776 m_runLengths.reserve(numElems);
3778 QPointF runPt = elements[0];
3780 for (
int i = 0; i < numElems; i++) {
3781 QPainterPath::Element e = elements[i];
3783 case QPainterPath::LineToElement:
3784 runLen += QLineF(runPt, e).length();
3787 case QPainterPath::CurveToElement: {
3788 Q_ASSERT(i < numElems - 2);
3789 QPainterPath::Element ee = elements[i + 2];
3790 runLen += QBezier::fromPoints(runPt, e, elements[i + 1], ee).length();
3794 case QPainterPath::MoveToElement:
3797 case QPainterPath::CurveToDataElement:
3800 m_runLengths.append(runLen);
3802 Q_ASSERT(m_runLengths.size() == elements.size());
3804 dirtyRunLengths =
false;
3807#ifndef QT_NO_DEBUG_STREAM
3810 QDebugStateSaver saver(s);
3811 s.nospace() <<
"QPainterPath: Element count=" << p.elementCount() << Qt::endl;
3812 const char *types[] = {
"MoveTo",
"LineTo",
"CurveTo",
"CurveToData"};
3813 for (
int i=0; i<p.elementCount(); ++i) {
3814 s.nospace() <<
" -> " << types[p.elementAt(i).type] <<
"(x=" << p.elementAt(i).x <<
", y=" << p.elementAt(i).y <<
')' << Qt::endl;
QBezier bezierAtT(const QPainterPath &path, qreal t, qreal *startingLength, qreal *bezierLength) const
QPainterPathStrokerPrivate()
Combined button and popup list for selecting options.
QDebug operator<<(QDebug dbg, const QFileInfo &fi)
static bool hasValidCoords(QRectF r)
static qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d)
void qt_path_stroke_move_to(qfixed x, qfixed y, void *data)
static bool qt_painterpath_check_crossing(const QPainterPath *path, const QRectF &rect)
static QBezier uncached_bezierAtT(const QPainterPath &path, qreal t, qreal *startingLength, qreal *bezierLength)
static bool qt_isect_curve_horizontal(const QBezier &bezier, qreal y, qreal x1, qreal x2, int depth=0)
static void qt_painterpath_isect_line(const QPointF &p1, const QPointF &p2, const QPointF &pos, int *winding)
static QRectF qt_painterpath_bezier_extrema(const QBezier &b)
#define QT_BEZIER_CHECK_T(bezier, t)
static bool qt_painterpath_isect_line_rect(qreal x1, qreal y1, qreal x2, qreal y2, const QRectF &rect)
#define QT_BEZIER_C(bezier, coord)
void qt_find_ellipse_coords(const QRectF &r, qreal angle, qreal length, QPointF *startPoint, QPointF *endPoint)
void qt_path_stroke_cubic_to(qfixed c1x, qfixed c1y, qfixed c2x, qfixed c2y, qfixed ex, qfixed ey, void *data)
#define QT_BEZIER_A(bezier, coord)
static bool epsilonCompare(const QPointF &a, const QPointF &b, const QSizeF &epsilon)
#define QT_BEZIER_B(bezier, coord)
static QT_BEGIN_NAMESPACE bool isValidCoord(qreal c)
static void qt_painterpath_isect_curve(const QBezier &bezier, const QPointF &pt, int *winding, int depth=0)
static bool qt_isect_curve_vertical(const QBezier &bezier, qreal x, qreal y1, qreal y2, int depth=0)
static bool pointOnEdge(const QRectF &rect, const QPointF &point)
QPainterPath qt_stroke_dash(const QPainterPath &path, qreal *dashes, int dashCount)
static bool hasValidCoords(QPointF p)
void qt_path_stroke_line_to(qfixed x, qfixed y, void *data)
QDataStream & operator<<(QDataStream &stream, const QImage &image)
[0]
QDataStream & operator>>(QDataStream &stream, QImage &image)