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
2501 bool errorDetected =
false;
2512 p.d_func()->elements.clear();
2513 for (
int i=0; i<size; ++i) {
2519 Q_ASSERT(type >= 0 && type <= 3);
2520 if (!isValidCoord(qreal(x)) || !isValidCoord(qreal(y))) {
2522 qWarning(
"QDataStream::operator>>: Invalid QPainterPath coordinates read, skipping it");
2524 errorDetected =
true;
2527 QPainterPath::Element elm = { qreal(x), qreal(y), QPainterPath::ElementType(type) };
2528 p.d_func()->elements.append(elm);
2530 s >> p.d_func()->cStart;
2533 Q_ASSERT(fillRule == Qt::OddEvenFill || fillRule == Qt::WindingFill);
2534 p.d_func()->hasWindingFill = (Qt::FillRule(fillRule) == Qt::WindingFill);
2535 if (errorDetected || p.d_func()->elements.isEmpty())
2543
2544
2548 ((QPainterPath *) data)->moveTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
2553 ((QPainterPath *) data)->lineTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
2557 qfixed c2x, qfixed c2y,
2558 qfixed ex, qfixed ey,
2561 ((QPainterPath *) data)->cubicTo(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y),
2562 qt_fixed_to_real(c2x), qt_fixed_to_real(c2y),
2563 qt_fixed_to_real(ex), qt_fixed_to_real(ey));
2567
2568
2569
2570
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2590
2591
2592
2593
2594
2595
2596
2597
2598
2599
2600
2601
2602
2603
2604
2605
2606
2607
2608
2609
2610
2611
2612
2613
2614
2615
2620 stroker.setMoveToHook(qt_path_stroke_move_to);
2621 stroker.setLineToHook(qt_path_stroke_line_to);
2622 stroker.setCubicToHook(qt_path_stroke_cubic_to);
2626
2627
2628QPainterPathStroker::QPainterPathStroker()
2629 : d_ptr(
new QPainterPathStrokerPrivate)
2634
2635
2636
2637
2638QPainterPathStroker::QPainterPathStroker(
const QPen &pen)
2639 : d_ptr(
new QPainterPathStrokerPrivate)
2641 setWidth(pen.widthF());
2642 setCapStyle(pen.capStyle());
2643 setJoinStyle(pen.joinStyle());
2644 setMiterLimit(pen.miterLimit());
2645 setDashOffset(pen.dashOffset());
2647 if (pen.style() == Qt::CustomDashLine)
2648 setDashPattern(pen.dashPattern());
2650 setDashPattern(pen.style());
2654
2655
2656QPainterPathStroker::~QPainterPathStroker()
2662
2663
2664
2665
2666
2667
2668
2669
2670
2671
2672
2673
2674QPainterPath QPainterPathStroker::createStroke(
const QPainterPath &path)
const
2676 QPainterPathStrokerPrivate *d =
const_cast<QPainterPathStrokerPrivate *>(d_func());
2677 QPainterPath stroke;
2680 if (d->dashPattern.isEmpty()) {
2681 d->stroker.strokePath(path, &stroke, QTransform());
2683 QDashStroker dashStroker(&d->stroker);
2684 dashStroker.setDashPattern(d->dashPattern);
2685 dashStroker.setDashOffset(d->dashOffset);
2686 dashStroker.setClipRect(d->stroker.clipRect());
2687 dashStroker.strokePath(path, &stroke, QTransform());
2689 stroke.setFillRule(Qt::WindingFill);
2694
2695
2696
2697
2698
2699void QPainterPathStroker::setWidth(qreal width)
2701 Q_D(QPainterPathStroker);
2704 d->stroker.setStrokeWidth(qt_real_to_fixed(width));
2708
2709
2710qreal QPainterPathStroker::width()
const
2712 return qt_fixed_to_real(d_func()->stroker.strokeWidth());
2717
2718
2719
2720
2721void QPainterPathStroker::setCapStyle(Qt::PenCapStyle style)
2723 d_func()->stroker.setCapStyle(style);
2728
2729
2730Qt::PenCapStyle QPainterPathStroker::capStyle()
const
2732 return d_func()->stroker.capStyle();
2736
2737
2738void QPainterPathStroker::setJoinStyle(Qt::PenJoinStyle style)
2740 d_func()->stroker.setJoinStyle(style);
2744
2745
2746Qt::PenJoinStyle QPainterPathStroker::joinStyle()
const
2748 return d_func()->stroker.joinStyle();
2752
2753
2754
2755
2756
2757
2758
2759
2760
2761void QPainterPathStroker::setMiterLimit(qreal limit)
2763 d_func()->stroker.setMiterLimit(qt_real_to_fixed(limit));
2767
2768
2769qreal QPainterPathStroker::miterLimit()
const
2771 return qt_fixed_to_real(d_func()->stroker.miterLimit());
2776
2777
2778
2779
2780
2781
2782
2783void QPainterPathStroker::setCurveThreshold(qreal threshold)
2785 d_func()->stroker.setCurveThreshold(qt_real_to_fixed(threshold));
2789
2790
2791
2792qreal QPainterPathStroker::curveThreshold()
const
2794 return qt_fixed_to_real(d_func()->stroker.curveThreshold());
2798
2799
2800void QPainterPathStroker::setDashPattern(Qt::PenStyle style)
2802 d_func()->dashPattern = QDashStroker::patternForStyle(style);
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821void QPainterPathStroker::setDashPattern(
const QList<qreal> &dashPattern)
2823 d_func()->dashPattern.clear();
2824 for (
int i=0; i<dashPattern.size(); ++i)
2825 d_func()->dashPattern << qt_real_to_fixed(dashPattern.at(i));
2829
2830
2831QList<qreal> QPainterPathStroker::dashPattern()
const
2833 return d_func()->dashPattern;
2837
2838
2839qreal QPainterPathStroker::dashOffset()
const
2841 return d_func()->dashOffset;
2845
2846
2847
2848
2849
2850void QPainterPathStroker::setDashOffset(qreal offset)
2852 d_func()->dashOffset = offset;
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870QPolygonF QPainterPath::toFillPolygon(
const QTransform &matrix)
const
2872 const QList<QPolygonF> flats = toSubpathPolygons(matrix);
2874 if (flats.isEmpty())
2876 QPointF first = flats.first().first();
2877 for (
int i=0; i<flats.size(); ++i) {
2878 polygon += flats.at(i);
2879 if (!flats.at(i).isClosed())
2880 polygon += flats.at(i).first();
2888
2889
2890
2891
2892
2893bool QPainterPath::isCachingEnabled()
const
2896 return d && d->cacheEnabled;
2900
2901
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911void QPainterPath::setCachingEnabled(
bool enabled)
2914 if (d_func()->cacheEnabled == enabled)
2917 QPainterPathPrivate *d = d_func();
2918 d->cacheEnabled = enabled;
2920 d->m_runLengths.clear();
2921 d->m_runLengths.squeeze();
2928 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
2932
2933
2934qreal QPainterPath::length()
const
2939 if (d->cacheEnabled) {
2940 if (d->dirtyRunLengths)
2941 d->computeRunLengths();
2942 return d->m_runLengths.last();
2946 for (
int i=1; i<d->elements.size(); ++i) {
2947 const Element &e = d->elements.at(i);
2954 len += QLineF(d->elements.at(i-1), e).length();
2957 case CurveToElement:
2959 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
2961 d->elements.at(i+1),
2962 d->elements.at(i+2));
2975
2976
2977
2978
2979
2980
2981
2982qreal QPainterPath::percentAtLength(qreal len)
const
2985 if (isEmpty() || len <= 0)
2988 qreal totalLength = length();
2989 if (len > totalLength)
2992 Q_ASSERT(totalLength != 0);
2994 if (d->cacheEnabled) {
2995 const int ei = qMax(d->elementAtT(len / totalLength), 1);
2997 const QPainterPath::Element &e = d->elements[ei];
2999 case QPainterPath::LineToElement:
3000 res = len / totalLength;
3002 case CurveToElement:
3004 QBezier b = QBezier::fromPoints(d->elements.at(ei-1),
3006 d->elements.at(ei+1),
3007 d->elements.at(ei+2));
3008 qreal prevLen = d->m_runLengths[ei - 1];
3009 qreal blen = d->m_runLengths[ei] - prevLen;
3010 qreal elemRes = b.tAtLength(len - prevLen);
3011 res = (elemRes * blen + prevLen) / totalLength;
3021 for (
int i=1; i<d->elements.size(); ++i) {
3022 const Element &e = d->elements.at(i);
3029 QLineF line(d->elements.at(i-1), e);
3030 qreal llen = line.length();
3032 if (curLen >= len) {
3033 return len/totalLength ;
3038 case CurveToElement:
3040 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
3042 d->elements.at(i+1),
3043 d->elements.at(i+2));
3044 qreal blen = b.length();
3045 qreal prevLen = curLen;
3048 if (curLen >= len) {
3049 qreal res = b.tAtLength(len - prevLen);
3050 return (res * blen + prevLen)/totalLength;
3065 qreal *bezierLength)
3067 *startingLength = 0;
3072 qreal totalLength = path.length();
3074 const int lastElement = path.elementCount() - 1;
3075 for (
int i=0; i <= lastElement; ++i) {
3076 const QPainterPath::Element &e = path.elementAt(i);
3079 case QPainterPath::MoveToElement:
3081 case QPainterPath::LineToElement:
3083 QLineF line(path.elementAt(i-1), e);
3084 qreal llen = line.length();
3086 if (i == lastElement || curLen/totalLength >= t) {
3087 *bezierLength = llen;
3088 QPointF a = path.elementAt(i-1);
3089 QPointF delta = e - a;
3090 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
3094 case QPainterPath::CurveToElement:
3096 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
3098 path.elementAt(i+1),
3099 path.elementAt(i+2));
3100 qreal blen = b.length();
3103 if (i + 2 == lastElement || curLen/totalLength >= t) {
3104 *bezierLength = blen;
3114 *startingLength = curLen;
3120 qreal *bezierLength)
const
3122 Q_ASSERT(t >= 0 && t <= 1);
3124 if (!path.isEmpty() && d->cacheEnabled) {
3125 const int ei = qMax(d->elementAtT(t), 1);
3126 const qreal prevRunLength = d->m_runLengths[ei - 1];
3127 *startingLength = prevRunLength;
3128 *bezierLength = d->m_runLengths[ei] - prevRunLength;
3129 const QPointF prev = d->elements[ei - 1];
3130 const QPainterPath::Element &e = d->elements[ei];
3132 case QPainterPath::LineToElement:
3134 QPointF delta = (e - prev) / 3;
3135 return QBezier::fromPoints(prev, prev + delta, prev + 2 * delta, e);
3137 case QPainterPath::CurveToElement:
3138 return QBezier::fromPoints(prev, e, elements[ei + 1], elements[ei + 2]);
3145 return uncached_bezierAtT(path, t, startingLength, bezierLength);
3149
3150
3151
3152
3153
3154
3155
3156
3157QPointF QPainterPath::pointAtPercent(qreal t)
const
3159 if (t < 0 || t > 1) {
3160 qWarning(
"QPainterPath::pointAtPercent accepts only values between 0 and 1");
3164 if (!d_ptr || d_ptr->elements.size() == 0)
3167 if (d_ptr->elements.size() == 1)
3168 return d_ptr->elements.at(0);
3170 qreal totalLength = length();
3172 qreal bezierLen = 0;
3173 QBezier b = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3174 Q_ASSERT(bezierLen != 0);
3175 qreal realT = (totalLength * t - curLen) / bezierLen;
3177 return b.pointAt(qBound(qreal(0), realT, qreal(1)));
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192qreal QPainterPath::angleAtPercent(qreal t)
const
3194 if (t < 0 || t > 1) {
3195 qWarning(
"QPainterPath::angleAtPercent accepts only values between 0 and 1");
3202 qreal totalLength = length();
3204 qreal bezierLen = 0;
3205 QBezier bez = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3206 Q_ASSERT(bezierLen != 0);
3207 qreal realT = (totalLength * t - curLen) / bezierLen;
3209 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3210 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3212 return QLineF(0, 0, m1, m2).angle();
3217
3218
3219
3220
3221
3222
3223
3224
3225qreal QPainterPath::slopeAtPercent(qreal t)
const
3227 if (t < 0 || t > 1) {
3228 qWarning(
"QPainterPath::slopeAtPercent accepts only values between 0 and 1");
3235 qreal totalLength = length();
3237 qreal bezierLen = 0;
3238 QBezier bez = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3239 Q_ASSERT(bezierLen != 0);
3240 qreal realT = (totalLength * t - curLen) / bezierLen;
3242 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3243 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3250 if (std::numeric_limits<qreal>::has_infinity) {
3251 slope = (m2 < 0) ? -std::numeric_limits<qreal>::infinity()
3252 : std::numeric_limits<qreal>::infinity();
3254 if (
sizeof(qreal) ==
sizeof(
double)) {
3255 return 1.79769313486231570e+308;
3257 return ((qreal)3.40282346638528860e+38);
3266
3267
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3282QPainterPath QPainterPath::trimmed(qreal fromFraction, qreal toFraction, qreal offset)
const
3288 if (!isCachingEnabled()) {
3289 QPainterPath copy(*
this);
3290 copy.setCachingEnabled(
true);
3291 return copy.trimmed(fromFraction, toFraction, offset);
3294 qreal f1 = qBound(qreal(0), fromFraction, qreal(1));
3295 qreal f2 = qBound(qreal(0), toFraction, qreal(1));
3296 if (qFuzzyIsNull(f1 - f2))
3297 return QPainterPath();
3300 if (qFuzzyCompare(f2 - f1, qreal(1)))
3304 res.setFillRule(fillRule());
3308 offset = std::modf(offset, &dummy);
3310 qreal of1 = f1 + offset;
3311 qreal of2 = f2 + offset;
3313 f1 = of1 < 0 ? of1 + 1 : of1;
3314 f2 = of2 + 1 > 1 ? of2 : of2 + 1;
3315 }
else if (offset > 0) {
3316 f1 = of1 - 1 < 0 ? of1 : of1 - 1;
3317 f2 = of2 > 1 ? of2 - 1 : of2;
3320 const bool wrapping = (f1 > f2);
3323 QPainterPathPrivate *d = d_func();
3324 if (d->dirtyRunLengths)
3325 d->computeRunLengths();
3326 const qreal totalLength = d->m_runLengths.last();
3327 if (qFuzzyIsNull(totalLength))
3330 const qreal l1 = f1 * totalLength;
3331 const qreal l2 = f2 * totalLength;
3332 const int e1 = d->elementAtLength(l1);
3333 const bool mustTrimE1 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e1), l1);
3334 const int e2 = d->elementAtLength(l2);
3335 const bool mustTrimE2 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e2), l2);
3338 if (e1 == e2 && !wrapping && mustTrimE1 && mustTrimE2) {
3340 d->appendSliceOfElement(&res, e1, l1, l2);
3344 d->appendEndOfElement(&res, e1, l1);
3346 res.moveTo(d->endPointOfElement(e1));
3349 int firstWholeElement = e1 + 1;
3350 int lastWholeElement = (mustTrimE2 ? e2 - 1 : e2);
3352 d->appendElementRange(&res, firstWholeElement, lastWholeElement);
3354 int lastIndex = d->elements.size() - 1;
3355 d->appendElementRange(&res, firstWholeElement, lastIndex);
3356 bool isClosed = (QPointF(d->elements.at(0)) == QPointF(d->elements.at(lastIndex)));
3358 d->appendElementRange(&res, (isClosed ? 1 : 0), lastWholeElement);
3363 d->appendStartOfElement(&res, e2, l2);
3370 qreal startLen, qreal endLen)
3372 Q_ASSERT(cacheEnabled);
3373 Q_ASSERT(!dirtyRunLengths);
3375 if (elemIdx <= 0 || elemIdx >= elements.size())
3378 const qreal prevLen = m_runLengths.at(elemIdx - 1);
3379 const qreal elemLen = m_runLengths.at(elemIdx) - prevLen;
3380 const qreal len1 = startLen - prevLen;
3381 const qreal len2 = endLen - prevLen;
3382 if (qFuzzyIsNull(elemLen))
3385 const QPointF pp = elements.at(elemIdx - 1);
3386 const QPainterPath::Element e = elements.at(elemIdx);
3389 QPointF p1 = (trimFlags & TrimStart) ? l.pointAt(len1 / elemLen) : pp;
3390 QPointF p2 = (trimFlags & TrimEnd) ? l.pointAt(len2 / elemLen) : e;
3394 }
else if (e.isCurveTo()) {
3395 Q_ASSERT(elemIdx < elements.size() - 2);
3396 QBezier b = QBezier::fromPoints(pp, e, elements.at(elemIdx + 1), elements.at(elemIdx + 2));
3397 qreal t1 = (trimFlags & TrimStart) ? b.tAtLength(len1) : 0.0;
3398 qreal t2 = (trimFlags & TrimEnd) ? b.tAtLength(len2) : 1.0;
3399 QBezier c = b.getSubRange(t1, t2);
3401 to->moveTo(c.pt1());
3402 to->cubicTo(c.pt2(), c.pt3(), c.pt4());
3410 if (first < 0 || first >= elements.size() || last < 0 || last >= elements.size())
3414 for (
int i = first; i <= last; i++) {
3415 const QPainterPath::Element &e = elements.at(i);
3417 case QPainterPath::MoveToElement:
3420 case QPainterPath::LineToElement:
3423 case QPainterPath::CurveToElement:
3424 Q_ASSERT(i < elements.size() - 2);
3425 to->cubicTo(e, elements.at(i + 1), elements.at(i + 2));
3437
3438
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449void QPainterPath::addRoundedRect(
const QRectF &rect, qreal xRadius, qreal yRadius,
3452 QRectF r = rect.normalized();
3457 if (mode == Qt::AbsoluteSize) {
3458 qreal w = r.width() / 2;
3459 qreal h = r.height() / 2;
3464 xRadius = 100 * qMin(xRadius, w) / w;
3469 yRadius = 100 * qMin(yRadius, h) / h;
3479 if (xRadius <= 0 || yRadius <= 0) {
3486 qreal w = r.width();
3487 qreal h = r.height();
3488 qreal rxx2 = w*xRadius/100;
3489 qreal ryy2 = h*yRadius/100;
3494 bool first = d_func()->elements.size() < 2;
3496 arcMoveTo(x, y, rxx2, ryy2, 180);
3497 arcTo(x, y, rxx2, ryy2, 180, -90);
3498 arcTo(x+w-rxx2, y, rxx2, ryy2, 90, -90);
3499 arcTo(x+w-rxx2, y+h-ryy2, rxx2, ryy2, 0, -90);
3500 arcTo(x, y+h-ryy2, rxx2, ryy2, 270, -90);
3503 d_func()->require_moveTo =
true;
3504 d_func()->convex = first;
3508
3509
3510
3511
3512
3513
3516
3517
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527QPainterPath QPainterPath::united(
const QPainterPath &p)
const
3529 if (isEmpty() || p.isEmpty())
3530 return isEmpty() ? p : *
this;
3531 QPathClipper clipper(*
this, p);
3532 return clipper.clip(QPathClipper::BoolOr);
3536
3537
3538
3539
3540
3541
3542QPainterPath QPainterPath::intersected(
const QPainterPath &p)
const
3544 if (isEmpty() || p.isEmpty())
3545 return QPainterPath();
3546 QPathClipper clipper(*
this, p);
3547 return clipper.clip(QPathClipper::BoolAnd);
3551
3552
3553
3554
3555
3556
3557
3558
3559
3560QPainterPath QPainterPath::subtracted(
const QPainterPath &p)
const
3562 if (isEmpty() || p.isEmpty())
3564 QPathClipper clipper(*
this, p);
3565 return clipper.clip(QPathClipper::BoolSub);
3569
3570
3571
3572
3573
3574
3575
3576
3577QPainterPath QPainterPath::simplified()
const
3581 QPathClipper clipper(*
this, QPainterPath());
3582 return clipper.clip(QPathClipper::Simplify);
3586
3587
3588
3589
3590
3591
3592
3593
3594
3595
3596bool QPainterPath::intersects(
const QPainterPath &p)
const
3598 if (p.elementCount() == 1)
3599 return contains(p.elementAt(0));
3600 if (isEmpty() || p.isEmpty())
3602 QPathClipper clipper(*
this, p);
3603 return clipper.intersect();
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618bool QPainterPath::contains(
const QPainterPath &p)
const
3620 if (p.elementCount() == 1)
3621 return contains(p.elementAt(0));
3622 if (isEmpty() || p.isEmpty())
3624 QPathClipper clipper(*
this, p);
3625 return clipper.contains();
3628void QPainterPath::setDirty(
bool dirty)
3630 d_func()->pathConverter.reset();
3631 d_func()->dirtyBounds = dirty;
3632 d_func()->dirtyControlBounds = dirty;
3633 d_func()->dirtyRunLengths = dirty;
3634 d_func()->convex =
false;
3637void QPainterPath::computeBoundingRect()
const
3639 QPainterPathPrivate *d = d_func();
3640 d->dirtyBounds =
false;
3642 d->bounds = QRect();
3646 qreal minx, maxx, miny, maxy;
3647 minx = maxx = d->elements.at(0).x;
3648 miny = maxy = d->elements.at(0).y;
3649 for (
int i=1; i<d->elements.size(); ++i) {
3650 const Element &e = d->elements.at(i);
3655 if (e.x > maxx) maxx = e.x;
3656 else if (e.x < minx) minx = e.x;
3657 if (e.y > maxy) maxy = e.y;
3658 else if (e.y < miny) miny = e.y;
3660 case CurveToElement:
3662 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
3664 d->elements.at(i+1),
3665 d->elements.at(i+2));
3666 QRectF r = qt_painterpath_bezier_extrema(b);
3667 qreal right = r.right();
3668 qreal bottom = r.bottom();
3669 if (r.x() < minx) minx = r.x();
3670 if (right > maxx) maxx = right;
3671 if (r.y() < miny) miny = r.y();
3672 if (bottom > maxy) maxy = bottom;
3680 d->bounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3684void QPainterPath::computeControlPointRect()
const
3686 QPainterPathPrivate *d = d_func();
3687 d->dirtyControlBounds =
false;
3689 d->controlBounds = QRect();
3693 qreal minx, maxx, miny, maxy;
3694 minx = maxx = d->elements.at(0).x;
3695 miny = maxy = d->elements.at(0).y;
3696 for (
int i=1; i<d->elements.size(); ++i) {
3697 const Element &e = d->elements.at(i);
3698 if (e.x > maxx) maxx = e.x;
3699 else if (e.x < minx) minx = e.x;
3700 if (e.y > maxy) maxy = e.y;
3701 else if (e.y < miny) miny = e.y;
3703 d->controlBounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3708 Q_ASSERT(!elements.isEmpty());
3710 m_runLengths.clear();
3711 const int numElems = elements.size();
3712 m_runLengths.reserve(numElems);
3714 QPointF runPt = elements[0];
3716 for (
int i = 0; i < numElems; i++) {
3717 QPainterPath::Element e = elements[i];
3719 case QPainterPath::LineToElement:
3720 runLen += QLineF(runPt, e).length();
3723 case QPainterPath::CurveToElement: {
3724 Q_ASSERT(i < numElems - 2);
3725 QPainterPath::Element ee = elements[i + 2];
3726 runLen += QBezier::fromPoints(runPt, e, elements[i + 1], ee).length();
3730 case QPainterPath::MoveToElement:
3733 case QPainterPath::CurveToDataElement:
3736 m_runLengths.append(runLen);
3738 Q_ASSERT(m_runLengths.size() == elements.size());
3740 dirtyRunLengths =
false;
3743#ifndef QT_NO_DEBUG_STREAM
3746 QDebugStateSaver saver(s);
3747 s.nospace() <<
"QPainterPath: Element count=" << p.elementCount() << Qt::endl;
3748 const char *types[] = {
"MoveTo",
"LineTo",
"CurveTo",
"CurveToData"};
3749 for (
int i=0; i<p.elementCount(); ++i) {
3750 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)