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 if (d->stroker.curveThresholdExplicitlySet())
2688 dashStroker.setCurveThreshold(d->stroker.curveThreshold());
2689 dashStroker.strokePath(path, &stroke, QTransform());
2691 stroke.setFillRule(Qt::WindingFill);
2696
2697
2698
2699
2700
2701void QPainterPathStroker::setWidth(qreal width)
2703 Q_D(QPainterPathStroker);
2706 d->stroker.setStrokeWidth(qt_real_to_fixed(width));
2710
2711
2712qreal QPainterPathStroker::width()
const
2714 return qt_fixed_to_real(d_func()->stroker.strokeWidth());
2719
2720
2721
2722
2723void QPainterPathStroker::setCapStyle(Qt::PenCapStyle style)
2725 d_func()->stroker.setCapStyle(style);
2730
2731
2732Qt::PenCapStyle QPainterPathStroker::capStyle()
const
2734 return d_func()->stroker.capStyle();
2738
2739
2740void QPainterPathStroker::setJoinStyle(Qt::PenJoinStyle style)
2742 d_func()->stroker.setJoinStyle(style);
2746
2747
2748Qt::PenJoinStyle QPainterPathStroker::joinStyle()
const
2750 return d_func()->stroker.joinStyle();
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763void QPainterPathStroker::setMiterLimit(qreal limit)
2765 d_func()->stroker.setMiterLimit(qt_real_to_fixed(limit));
2769
2770
2771qreal QPainterPathStroker::miterLimit()
const
2773 return qt_fixed_to_real(d_func()->stroker.miterLimit());
2778
2779
2780
2781
2782
2783
2784
2785void QPainterPathStroker::setCurveThreshold(qreal threshold)
2787 d_func()->stroker.setCurveThreshold(qt_real_to_fixed(threshold));
2791
2792
2793
2794qreal QPainterPathStroker::curveThreshold()
const
2796 return qt_fixed_to_real(d_func()->stroker.curveThreshold());
2800
2801
2802void QPainterPathStroker::setDashPattern(Qt::PenStyle style)
2804 d_func()->dashPattern = QDashStroker::patternForStyle(style);
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823void QPainterPathStroker::setDashPattern(
const QList<qreal> &dashPattern)
2825 d_func()->dashPattern.clear();
2826 for (
int i=0; i<dashPattern.size(); ++i)
2827 d_func()->dashPattern << qt_real_to_fixed(dashPattern.at(i));
2831
2832
2833QList<qreal> QPainterPathStroker::dashPattern()
const
2835 return d_func()->dashPattern;
2839
2840
2841qreal QPainterPathStroker::dashOffset()
const
2843 return d_func()->dashOffset;
2847
2848
2849
2850
2851
2852void QPainterPathStroker::setDashOffset(qreal offset)
2854 d_func()->dashOffset = offset;
2858
2859
2860
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872QPolygonF QPainterPath::toFillPolygon(
const QTransform &matrix)
const
2874 const QList<QPolygonF> flats = toSubpathPolygons(matrix);
2876 if (flats.isEmpty())
2878 QPointF first = flats.first().first();
2879 for (
int i=0; i<flats.size(); ++i) {
2880 polygon += flats.at(i);
2881 if (!flats.at(i).isClosed())
2882 polygon += flats.at(i).first();
2890
2891
2892
2893
2894
2895bool QPainterPath::isCachingEnabled()
const
2898 return d && d->cacheEnabled;
2902
2903
2904
2905
2906
2907
2908
2909
2910
2911
2912
2913void QPainterPath::setCachingEnabled(
bool enabled)
2916 if (d_func()->cacheEnabled == enabled)
2919 QPainterPathPrivate *d = d_func();
2920 d->cacheEnabled = enabled;
2922 d->m_runLengths.clear();
2923 d->m_runLengths.squeeze();
2930 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
2934
2935
2936qreal QPainterPath::length()
const
2941 if (d->cacheEnabled) {
2942 if (d->dirtyRunLengths)
2943 d->computeRunLengths();
2944 return d->m_runLengths.last();
2948 for (
int i=1; i<d->elements.size(); ++i) {
2949 const Element &e = d->elements.at(i);
2956 len += QLineF(d->elements.at(i-1), e).length();
2959 case CurveToElement:
2961 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
2963 d->elements.at(i+1),
2964 d->elements.at(i+2));
2977
2978
2979
2980
2981
2982
2983
2984qreal QPainterPath::percentAtLength(qreal len)
const
2987 if (isEmpty() || len <= 0)
2990 qreal totalLength = length();
2991 if (len > totalLength)
2994 Q_ASSERT(totalLength != 0);
2996 if (d->cacheEnabled) {
2997 const int ei = qMax(d->elementAtT(len / totalLength), 1);
2999 const QPainterPath::Element &e = d->elements[ei];
3001 case QPainterPath::LineToElement:
3002 res = len / totalLength;
3004 case CurveToElement:
3006 QBezier b = QBezier::fromPoints(d->elements.at(ei-1),
3008 d->elements.at(ei+1),
3009 d->elements.at(ei+2));
3010 qreal prevLen = d->m_runLengths[ei - 1];
3011 qreal blen = d->m_runLengths[ei] - prevLen;
3012 qreal elemRes = b.tAtLength(len - prevLen);
3013 res = (elemRes * blen + prevLen) / totalLength;
3023 for (
int i=1; i<d->elements.size(); ++i) {
3024 const Element &e = d->elements.at(i);
3031 QLineF line(d->elements.at(i-1), e);
3032 qreal llen = line.length();
3034 if (curLen >= len) {
3035 return len/totalLength ;
3040 case CurveToElement:
3042 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
3044 d->elements.at(i+1),
3045 d->elements.at(i+2));
3046 qreal blen = b.length();
3047 qreal prevLen = curLen;
3050 if (curLen >= len) {
3051 qreal res = b.tAtLength(len - prevLen);
3052 return (res * blen + prevLen)/totalLength;
3067 qreal *bezierLength)
3069 *startingLength = 0;
3074 qreal totalLength = path.length();
3076 const int lastElement = path.elementCount() - 1;
3077 for (
int i=0; i <= lastElement; ++i) {
3078 const QPainterPath::Element &e = path.elementAt(i);
3081 case QPainterPath::MoveToElement:
3083 case QPainterPath::LineToElement:
3085 QLineF line(path.elementAt(i-1), e);
3086 qreal llen = line.length();
3088 if (i == lastElement || curLen/totalLength >= t) {
3089 *bezierLength = llen;
3090 QPointF a = path.elementAt(i-1);
3091 QPointF delta = e - a;
3092 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
3096 case QPainterPath::CurveToElement:
3098 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
3100 path.elementAt(i+1),
3101 path.elementAt(i+2));
3102 qreal blen = b.length();
3105 if (i + 2 == lastElement || curLen/totalLength >= t) {
3106 *bezierLength = blen;
3116 *startingLength = curLen;
3122 qreal *bezierLength)
const
3124 Q_ASSERT(t >= 0 && t <= 1);
3126 if (!path.isEmpty() && d->cacheEnabled) {
3127 const int ei = qMax(d->elementAtT(t), 1);
3128 const qreal prevRunLength = d->m_runLengths[ei - 1];
3129 *startingLength = prevRunLength;
3130 *bezierLength = d->m_runLengths[ei] - prevRunLength;
3131 const QPointF prev = d->elements[ei - 1];
3132 const QPainterPath::Element &e = d->elements[ei];
3134 case QPainterPath::LineToElement:
3136 QPointF delta = (e - prev) / 3;
3137 return QBezier::fromPoints(prev, prev + delta, prev + 2 * delta, e);
3139 case QPainterPath::CurveToElement:
3140 return QBezier::fromPoints(prev, e, elements[ei + 1], elements[ei + 2]);
3147 return uncached_bezierAtT(path, t, startingLength, bezierLength);
3151
3152
3153
3154
3155
3156
3157
3158
3159QPointF QPainterPath::pointAtPercent(qreal t)
const
3161 if (t < 0 || t > 1) {
3162 qWarning(
"QPainterPath::pointAtPercent accepts only values between 0 and 1");
3166 if (!d_ptr || d_ptr->elements.size() == 0)
3169 if (d_ptr->elements.size() == 1)
3170 return d_ptr->elements.at(0);
3172 qreal totalLength = length();
3174 qreal bezierLen = 0;
3175 QBezier b = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3176 Q_ASSERT(bezierLen != 0);
3177 qreal realT = (totalLength * t - curLen) / bezierLen;
3179 return b.pointAt(qBound(qreal(0), realT, qreal(1)));
3183
3184
3185
3186
3187
3188
3189
3190
3191
3192
3193
3194qreal QPainterPath::angleAtPercent(qreal t)
const
3196 if (t < 0 || t > 1) {
3197 qWarning(
"QPainterPath::angleAtPercent accepts only values between 0 and 1");
3204 qreal totalLength = length();
3206 qreal bezierLen = 0;
3207 QBezier bez = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3208 Q_ASSERT(bezierLen != 0);
3209 qreal realT = (totalLength * t - curLen) / bezierLen;
3211 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3212 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3214 return QLineF(0, 0, m1, m2).angle();
3219
3220
3221
3222
3223
3224
3225
3226
3227qreal QPainterPath::slopeAtPercent(qreal t)
const
3229 if (t < 0 || t > 1) {
3230 qWarning(
"QPainterPath::slopeAtPercent accepts only values between 0 and 1");
3237 qreal totalLength = length();
3239 qreal bezierLen = 0;
3240 QBezier bez = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3241 Q_ASSERT(bezierLen != 0);
3242 qreal realT = (totalLength * t - curLen) / bezierLen;
3244 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3245 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3252 if (std::numeric_limits<qreal>::has_infinity) {
3253 slope = (m2 < 0) ? -std::numeric_limits<qreal>::infinity()
3254 : std::numeric_limits<qreal>::infinity();
3256 if (
sizeof(qreal) ==
sizeof(
double)) {
3257 return 1.79769313486231570e+308;
3259 return ((qreal)3.40282346638528860e+38);
3268
3269
3270
3271
3272
3273
3274
3275
3276
3277
3278
3279
3280
3281
3282
3284QPainterPath QPainterPath::trimmed(qreal fromFraction, qreal toFraction, qreal offset)
const
3290 if (!isCachingEnabled()) {
3291 QPainterPath copy(*
this);
3292 copy.setCachingEnabled(
true);
3293 return copy.trimmed(fromFraction, toFraction, offset);
3296 qreal f1 = qBound(qreal(0), fromFraction, qreal(1));
3297 qreal f2 = qBound(qreal(0), toFraction, qreal(1));
3298 if (qFuzzyIsNull(f1 - f2))
3299 return QPainterPath();
3302 if (qFuzzyCompare(f2 - f1, qreal(1)))
3306 res.setFillRule(fillRule());
3310 offset = std::modf(offset, &dummy);
3312 qreal of1 = f1 + offset;
3313 qreal of2 = f2 + offset;
3315 f1 = of1 < 0 ? of1 + 1 : of1;
3316 f2 = of2 + 1 > 1 ? of2 : of2 + 1;
3317 }
else if (offset > 0) {
3318 f1 = of1 - 1 < 0 ? of1 : of1 - 1;
3319 f2 = of2 > 1 ? of2 - 1 : of2;
3322 const bool wrapping = (f1 > f2);
3325 QPainterPathPrivate *d = d_func();
3326 if (d->dirtyRunLengths)
3327 d->computeRunLengths();
3328 const qreal totalLength = d->m_runLengths.last();
3329 if (qFuzzyIsNull(totalLength))
3332 const qreal l1 = f1 * totalLength;
3333 const qreal l2 = f2 * totalLength;
3334 const int e1 = d->elementAtLength(l1);
3335 const bool mustTrimE1 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e1), l1);
3336 const int e2 = d->elementAtLength(l2);
3337 const bool mustTrimE2 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e2), l2);
3340 if (e1 == e2 && !wrapping && mustTrimE1 && mustTrimE2) {
3342 d->appendSliceOfElement(&res, e1, l1, l2);
3346 d->appendEndOfElement(&res, e1, l1);
3348 res.moveTo(d->endPointOfElement(e1));
3351 int firstWholeElement = e1 + 1;
3352 int lastWholeElement = (mustTrimE2 ? e2 - 1 : e2);
3354 d->appendElementRange(&res, firstWholeElement, lastWholeElement);
3356 int lastIndex = d->elements.size() - 1;
3357 d->appendElementRange(&res, firstWholeElement, lastIndex);
3358 bool isClosed = (QPointF(d->elements.at(0)) == QPointF(d->elements.at(lastIndex)));
3360 d->appendElementRange(&res, (isClosed ? 1 : 0), lastWholeElement);
3365 d->appendStartOfElement(&res, e2, l2);
3372 qreal startLen, qreal endLen)
3374 Q_ASSERT(cacheEnabled);
3375 Q_ASSERT(!dirtyRunLengths);
3377 if (elemIdx <= 0 || elemIdx >= elements.size())
3380 const qreal prevLen = m_runLengths.at(elemIdx - 1);
3381 const qreal elemLen = m_runLengths.at(elemIdx) - prevLen;
3382 const qreal len1 = startLen - prevLen;
3383 const qreal len2 = endLen - prevLen;
3384 if (qFuzzyIsNull(elemLen))
3387 const QPointF pp = elements.at(elemIdx - 1);
3388 const QPainterPath::Element e = elements.at(elemIdx);
3391 QPointF p1 = (trimFlags & TrimStart) ? l.pointAt(len1 / elemLen) : pp;
3392 QPointF p2 = (trimFlags & TrimEnd) ? l.pointAt(len2 / elemLen) : e;
3396 }
else if (e.isCurveTo()) {
3397 Q_ASSERT(elemIdx < elements.size() - 2);
3398 QBezier b = QBezier::fromPoints(pp, e, elements.at(elemIdx + 1), elements.at(elemIdx + 2));
3399 qreal t1 = (trimFlags & TrimStart) ? b.tAtLength(len1) : 0.0;
3400 qreal t2 = (trimFlags & TrimEnd) ? b.tAtLength(len2) : 1.0;
3401 QBezier c = b.getSubRange(t1, t2);
3403 to->moveTo(c.pt1());
3404 to->cubicTo(c.pt2(), c.pt3(), c.pt4());
3412 if (first < 0 || first >= elements.size() || last < 0 || last >= elements.size())
3416 for (
int i = first; i <= last; i++) {
3417 const QPainterPath::Element &e = elements.at(i);
3419 case QPainterPath::MoveToElement:
3422 case QPainterPath::LineToElement:
3425 case QPainterPath::CurveToElement:
3426 Q_ASSERT(i < elements.size() - 2);
3427 to->cubicTo(e, elements.at(i + 1), elements.at(i + 2));
3439
3440
3441
3442
3443
3444
3445
3446
3447
3448
3449
3450
3451void QPainterPath::addRoundedRect(
const QRectF &rect, qreal xRadius, qreal yRadius,
3454 QRectF r = rect.normalized();
3459 if (mode == Qt::AbsoluteSize) {
3460 qreal w = r.width() / 2;
3461 qreal h = r.height() / 2;
3466 xRadius = 100 * qMin(xRadius, w) / w;
3471 yRadius = 100 * qMin(yRadius, h) / h;
3481 if (xRadius <= 0 || yRadius <= 0) {
3488 qreal w = r.width();
3489 qreal h = r.height();
3490 qreal rxx2 = w*xRadius/100;
3491 qreal ryy2 = h*yRadius/100;
3496 bool first = d_func()->elements.size() < 2;
3498 arcMoveTo(x, y, rxx2, ryy2, 180);
3499 arcTo(x, y, rxx2, ryy2, 180, -90);
3500 arcTo(x+w-rxx2, y, rxx2, ryy2, 90, -90);
3501 arcTo(x+w-rxx2, y+h-ryy2, rxx2, ryy2, 0, -90);
3502 arcTo(x, y+h-ryy2, rxx2, ryy2, 270, -90);
3505 d_func()->require_moveTo =
true;
3506 d_func()->convex = first;
3510
3511
3512
3513
3514
3515
3518
3519
3520
3521
3522
3523
3524
3525
3526
3527
3528
3529QPainterPath QPainterPath::united(
const QPainterPath &p)
const
3531 if (isEmpty() || p.isEmpty())
3532 return isEmpty() ? p : *
this;
3533 QPathClipper clipper(*
this, p);
3534 return clipper.clip(QPathClipper::BoolOr);
3538
3539
3540
3541
3542
3543
3544QPainterPath QPainterPath::intersected(
const QPainterPath &p)
const
3546 if (isEmpty() || p.isEmpty())
3547 return QPainterPath();
3548 QPathClipper clipper(*
this, p);
3549 return clipper.clip(QPathClipper::BoolAnd);
3553
3554
3555
3556
3557
3558
3559
3560
3561
3562QPainterPath QPainterPath::subtracted(
const QPainterPath &p)
const
3564 if (isEmpty() || p.isEmpty())
3566 QPathClipper clipper(*
this, p);
3567 return clipper.clip(QPathClipper::BoolSub);
3571
3572
3573
3574
3575
3576
3577
3578
3579QPainterPath QPainterPath::simplified()
const
3583 QPathClipper clipper(*
this, QPainterPath());
3584 return clipper.clip(QPathClipper::Simplify);
3588
3589
3590
3591
3592
3593
3594
3595
3596
3597
3598bool QPainterPath::intersects(
const QPainterPath &p)
const
3600 if (p.elementCount() == 1)
3601 return contains(p.elementAt(0));
3602 if (isEmpty() || p.isEmpty())
3604 QPathClipper clipper(*
this, p);
3605 return clipper.intersect();
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620bool QPainterPath::contains(
const QPainterPath &p)
const
3622 if (p.elementCount() == 1)
3623 return contains(p.elementAt(0));
3624 if (isEmpty() || p.isEmpty())
3626 QPathClipper clipper(*
this, p);
3627 return clipper.contains();
3630void QPainterPath::setDirty(
bool dirty)
3632 d_func()->pathConverter.reset();
3633 d_func()->dirtyBounds = dirty;
3634 d_func()->dirtyControlBounds = dirty;
3635 d_func()->dirtyRunLengths = dirty;
3636 d_func()->convex =
false;
3639void QPainterPath::computeBoundingRect()
const
3641 QPainterPathPrivate *d = d_func();
3642 d->dirtyBounds =
false;
3644 d->bounds = QRect();
3648 qreal minx, maxx, miny, maxy;
3649 minx = maxx = d->elements.at(0).x;
3650 miny = maxy = d->elements.at(0).y;
3651 for (
int i=1; i<d->elements.size(); ++i) {
3652 const Element &e = d->elements.at(i);
3657 if (e.x > maxx) maxx = e.x;
3658 else if (e.x < minx) minx = e.x;
3659 if (e.y > maxy) maxy = e.y;
3660 else if (e.y < miny) miny = e.y;
3662 case CurveToElement:
3664 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
3666 d->elements.at(i+1),
3667 d->elements.at(i+2));
3668 QRectF r = qt_painterpath_bezier_extrema(b);
3669 qreal right = r.right();
3670 qreal bottom = r.bottom();
3671 if (r.x() < minx) minx = r.x();
3672 if (right > maxx) maxx = right;
3673 if (r.y() < miny) miny = r.y();
3674 if (bottom > maxy) maxy = bottom;
3682 d->bounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3686void QPainterPath::computeControlPointRect()
const
3688 QPainterPathPrivate *d = d_func();
3689 d->dirtyControlBounds =
false;
3691 d->controlBounds = QRect();
3695 qreal minx, maxx, miny, maxy;
3696 minx = maxx = d->elements.at(0).x;
3697 miny = maxy = d->elements.at(0).y;
3698 for (
int i=1; i<d->elements.size(); ++i) {
3699 const Element &e = d->elements.at(i);
3700 if (e.x > maxx) maxx = e.x;
3701 else if (e.x < minx) minx = e.x;
3702 if (e.y > maxy) maxy = e.y;
3703 else if (e.y < miny) miny = e.y;
3705 d->controlBounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3710 Q_ASSERT(!elements.isEmpty());
3712 m_runLengths.clear();
3713 const int numElems = elements.size();
3714 m_runLengths.reserve(numElems);
3716 QPointF runPt = elements[0];
3718 for (
int i = 0; i < numElems; i++) {
3719 QPainterPath::Element e = elements[i];
3721 case QPainterPath::LineToElement:
3722 runLen += QLineF(runPt, e).length();
3725 case QPainterPath::CurveToElement: {
3726 Q_ASSERT(i < numElems - 2);
3727 QPainterPath::Element ee = elements[i + 2];
3728 runLen += QBezier::fromPoints(runPt, e, elements[i + 1], ee).length();
3732 case QPainterPath::MoveToElement:
3735 case QPainterPath::CurveToDataElement:
3738 m_runLengths.append(runLen);
3740 Q_ASSERT(m_runLengths.size() == elements.size());
3742 dirtyRunLengths =
false;
3745#ifndef QT_NO_DEBUG_STREAM
3748 QDebugStateSaver saver(s);
3749 s.nospace() <<
"QPainterPath: Element count=" << p.elementCount() << Qt::endl;
3750 const char *types[] = {
"MoveTo",
"LineTo",
"CurveTo",
"CurveToData"};
3751 for (
int i=0; i<p.elementCount(); ++i) {
3752 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)