13#include <qtextlayout.h>
14#include <qvarlengtharray.h>
17#include <private/qbezier_p.h>
18#include <private/qfontengine_p.h>
19#include <private/qnumeric_p.h>
20#include <private/qobject_p.h>
21#include <private/qpathclipper_p.h>
22#include <private/qstroker_p.h>
23#include <private/qtextengine_p.h>
30#include <performance.h>
41 if (
sizeof(qreal) >=
sizeof(
double))
42 return qIsFinite(c) && fabs(c) < 1e128;
44 return qIsFinite(c) && fabsf(
float(c)) < 1e16f;
49 return isValidCoord(p.x()) && isValidCoord(p.y());
54 return isValidCoord(r.x()) && isValidCoord(r.y()) && isValidCoord(r.width()) && isValidCoord(r.height());
68 QPointF* startPoint, QPointF *endPoint)
72 *startPoint = QPointF();
74 *endPoint = QPointF();
78 qreal w2 = r.width() / 2;
79 qreal h2 = r.height() / 2;
81 qreal angles[2] = { angle, angle + length };
82 QPointF *points[2] = { startPoint, endPoint };
84 for (
int i = 0; i < 2; ++i) {
88 qreal theta = angles[i] - 360 * qFloor(angles[i] / 360);
91 int quadrant =
int(t);
94 t = qt_t_for_arc_angle(90 * t);
101 QBezier::coefficients(t, a, b, c, d);
102 QPointF p(a + b + c*QT_PATH_KAPPA, d + c + b*QT_PATH_KAPPA);
105 if (quadrant == 1 || quadrant == 2)
109 if (quadrant == 0 || quadrant == 1)
112 *points[i] = r.center() + QPointF(w2 * p.x(), h2 * p.y());
117static void qt_debug_path(
const QPainterPath &path)
119 const char *names[] = {
126 printf(
"\nQPainterPath: elementCount=%d\n", path.elementCount());
127 for (
int i=0; i<path.elementCount(); ++i) {
128 const QPainterPath::Element &e = path.elementAt(i);
129 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
130 printf(
" - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
136
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
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
343
344
345
346
347
350
351
352
353
354
357
358
359
360
361
364
365
366
367
368
369
370
371
374
375
376
377
378
379
380
381
384
385
386
387
388
389
392
393
394
395
396
397
400
401
402
403
404
405
406
409
410
411
412
413
414
417
418
419
420
421
422
423
426
427
428
429
430
431
432
433
436
437
438
439
440
441
442
445
446
447
448
449
450
452int QPainterPath::elementCount()
const
454 return d_ptr ? d_ptr->elements.size() : 0;
458
459
460
461
462
463
465QPainterPath::Element QPainterPath::elementAt(
int i)
const
468 Q_ASSERT(i >= 0 && i < elementCount());
469 return d_ptr->elements.at(i);
473
474
475
476
477
478
480void QPainterPath::setElementPositionAt(
int i, qreal x, qreal y)
483 Q_ASSERT(i >= 0 && i < elementCount());
485 QPainterPath::Element &e = d_ptr->elements[i];
492
493
494
495
496
499
500
501QPainterPath::QPainterPath()
noexcept
507
508
509
510
511
512
513QPainterPath::QPainterPath(
const QPainterPath &other)
514 : d_ptr(other.d_ptr ?
new QPainterPathPrivate(*other.d_ptr) :
nullptr)
519
520
521
522
523
524
525
528
529
530
532QPainterPath::QPainterPath(
const QPointF &startPoint)
533 : d_ptr(
new QPainterPathPrivate(startPoint))
538
539
540void QPainterPath::ensureData_helper()
542 Q_ASSERT(d_ptr ==
nullptr);
543 QPainterPathPrivate *data =
new QPainterPathPrivate;
544 data->elements.reserve(16);
545 QPainterPath::Element e = { 0, 0, QPainterPath::MoveToElement };
548 Q_ASSERT(d_ptr !=
nullptr);
552
553
554
555
556
557
558QPainterPath &QPainterPath::operator=(
const QPainterPath &other)
560 QPainterPath copy(other);
566
567
568
569
570
571
574
575
576
577
580
581
582QPainterPath::~QPainterPath()
588
589
590
591
592
593
594
595void QPainterPath::clear()
602 d_func()->elements.append( {0, 0, MoveToElement} );
606
607
608
609
610
611
612
613void QPainterPath::reserve(
int size)
616 if ((!d && size > 0) || (d && d->elements.capacity() < size)) {
619 d_func()->elements.reserve(size);
624
625
626
627
628
629int QPainterPath::capacity()
const
633 return d->elements.capacity();
639
640
641
642
643
644
645
646
647
648
649void QPainterPath::closeSubpath()
652 printf(
"QPainterPath::closeSubpath()\n");
662
663
664
665
666
667
668
671
672
673
674
675
676
677
678
679void QPainterPath::moveTo(
const QPointF &p)
682 printf(
"QPainterPath::moveTo() (%.2f,%.2f)\n", p.x(), p.y());
685 if (!hasValidCoords(p)) {
687 qWarning(
"QPainterPath::moveTo: Adding point with invalid coordinates, ignoring call");
695 QPainterPathPrivate *d = d_func();
696 Q_ASSERT(!d->elements.isEmpty());
698 d->require_moveTo =
false;
700 if (d->elements.constLast().type == MoveToElement) {
701 d->elements.last().x = p.x();
702 d->elements.last().y = p.y();
704 Element elm = { p.x(), p.y(), MoveToElement };
705 d->elements.append(elm);
707 d->cStart = d->elements.size() - 1;
711
712
713
714
715
716
717
720
721
722
723
724
725
726
727
728
729void QPainterPath::lineTo(
const QPointF &p)
732 printf(
"QPainterPath::lineTo() (%.2f,%.2f)\n", p.x(), p.y());
735 if (!hasValidCoords(p)) {
737 qWarning(
"QPainterPath::lineTo: Adding point with invalid coordinates, ignoring call");
745 QPainterPathPrivate *d = d_func();
746 Q_ASSERT(!d->elements.isEmpty());
748 if (p == QPointF(d->elements.constLast()))
750 Element elm = { p.x(), p.y(), LineToElement };
751 d->elements.append(elm);
753 d->convex = d->elements.size() == 3 || (d->elements.size() == 4 && d->isClosed());
757
758
759
760
761
762
763
764
765
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787void QPainterPath::cubicTo(
const QPointF &c1,
const QPointF &c2,
const QPointF &e)
790 printf(
"QPainterPath::cubicTo() (%.2f,%.2f), (%.2f,%.2f), (%.2f,%.2f)\n",
791 c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y());
794 if (!hasValidCoords(c1) || !hasValidCoords(c2) || !hasValidCoords(e)) {
796 qWarning(
"QPainterPath::cubicTo: Adding point with invalid coordinates, ignoring call");
804 QPainterPathPrivate *d = d_func();
805 Q_ASSERT(!d->elements.isEmpty());
810 if (d->elements.constLast() == c1 && c1 == c2 && c2 == e)
815 Element ce1 = { c1.x(), c1.y(), CurveToElement };
816 Element ce2 = { c2.x(), c2.y(), CurveToDataElement };
817 Element ee = { e.x(), e.y(), CurveToDataElement };
818 d->elements << ce1 << ce2 << ee;
822
823
824
825
826
827
828
829
832
833
834
835
836
837
838
839
840
841
842
843void QPainterPath::quadTo(
const QPointF &c,
const QPointF &e)
846 printf(
"QPainterPath::quadTo() (%.2f,%.2f), (%.2f,%.2f)\n",
847 c.x(), c.y(), e.x(), e.y());
850 if (!hasValidCoords(c) || !hasValidCoords(e)) {
852 qWarning(
"QPainterPath::quadTo: Adding point with invalid coordinates, ignoring call");
861 Q_ASSERT(!d->elements.isEmpty());
862 const QPainterPath::Element &elm = d->elements.at(elementCount()-1);
863 QPointF prev(elm.x, elm.y);
867 if (prev == c && c == e)
870 QPointF c1((prev.x() + 2*c.x()) / 3, (prev.y() + 2*c.y()) / 3);
871 QPointF c2((e.x() + 2*c.x()) / 3, (e.y() + 2*c.y()) / 3);
876
877
878
879
880
881
882
883
884
885
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914void QPainterPath::arcTo(
const QRectF &rect, qreal startAngle, qreal sweepLength)
917 printf(
"QPainterPath::arcTo() (%.2f, %.2f, %.2f, %.2f, angle=%.2f, sweep=%.2f\n",
918 rect.x(), rect.y(), rect.width(), rect.height(), startAngle, sweepLength);
921 if (!hasValidCoords(rect) || !isValidCoord(startAngle) || !isValidCoord(sweepLength)) {
923 qWarning(
"QPainterPath::arcTo: Adding point with invalid coordinates, ignoring call");
936 QPointF curve_start = qt_curves_for_arc(rect, startAngle, sweepLength, pts, &point_count);
939 for (
int i=0; i<point_count; i+=3) {
940 cubicTo(pts[i].x(), pts[i].y(),
941 pts[i+1].x(), pts[i+1].y(),
942 pts[i+2].x(), pts[i+2].y());
949
950
951
952
953
954
955
959
960
961
962
963
964
965
966
967
968
969
971void QPainterPath::arcMoveTo(
const QRectF &rect, qreal angle)
977 qt_find_ellipse_coords(rect, angle, 0, &pt,
nullptr);
984
985
986
987
988QPointF QPainterPath::currentPosition()
const
990 return !d_ptr || d_func()->elements.isEmpty()
992 : QPointF(d_func()->elements.constLast().x, d_func()->elements.constLast().y);
997
998
999
1000
1001
1002
1003
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024void QPainterPath::addRect(
const QRectF &r)
1026 if (!hasValidCoords(r)) {
1028 qWarning(
"QPainterPath::addRect: Adding point with invalid coordinates, ignoring call");
1039 bool first = d_func()->elements.size() < 2;
1041 moveTo(r.x(), r.y());
1043 Element l1 = { r.x() + r.width(), r.y(), LineToElement };
1044 Element l2 = { r.x() + r.width(), r.y() + r.height(), LineToElement };
1045 Element l3 = { r.x(), r.y() + r.height(), LineToElement };
1046 Element l4 = { r.x(), r.y(), LineToElement };
1048 d_func()->elements << l1 << l2 << l3 << l4;
1049 d_func()->require_moveTo =
true;
1050 d_func()->convex = first;
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070void QPainterPath::addPolygon(
const QPolygonF &polygon)
1072 if (polygon.isEmpty())
1078 moveTo(polygon.constFirst());
1079 for (
int i=1; i<polygon.size(); ++i) {
1080 Element elm = { polygon.at(i).x(), polygon.at(i).y(), LineToElement };
1081 d_func()->elements << elm;
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104void QPainterPath::addEllipse(
const QRectF &boundingRect)
1106 if (!hasValidCoords(boundingRect)) {
1108 qWarning(
"QPainterPath::addEllipse: Adding point with invalid coordinates, ignoring call");
1113 if (boundingRect.isNull())
1119 bool first = d_func()->elements.size() < 2;
1123 QPointF start = qt_curves_for_arc(boundingRect, 0, -360, pts, &point_count);
1126 cubicTo(pts[0], pts[1], pts[2]);
1127 cubicTo(pts[3], pts[4], pts[5]);
1128 cubicTo(pts[6], pts[7], pts[8]);
1129 cubicTo(pts[9], pts[10], pts[11]);
1130 d_func()->require_moveTo =
true;
1132 d_func()->convex = first;
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156void QPainterPath::addText(
const QPointF &point,
const QFont &f,
const QString &text)
1164 QTextLayout layout(text, f);
1165 layout.setCacheEnabled(
true);
1167 QTextOption opt = layout.textOption();
1168 opt.setUseDesignMetrics(
true);
1169 layout.setTextOption(opt);
1171 QTextEngine *eng = layout.engine();
1172 layout.beginLayout();
1173 QTextLine line = layout.createLine();
1176 const QScriptLine &sl = eng->lines[0];
1177 if (!sl.length || !eng->layoutData)
1180 int nItems = eng->layoutData->items.size();
1185 QVarLengthArray<
int> visualOrder(nItems);
1186 QVarLengthArray<uchar> levels(nItems);
1187 for (
int i = 0; i < nItems; ++i)
1188 levels[i] = eng->layoutData->items.at(i).analysis.bidiLevel;
1189 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
1191 for (
int i = 0; i < nItems; ++i) {
1192 int item = visualOrder[i];
1193 const QScriptItem &si = eng->layoutData->items.at(item);
1195 if (si.analysis.flags < QScriptAnalysis::TabOrObject) {
1196 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
1197 QFontEngine *fe = eng->fontEngine(si);
1199 fe->addOutlineToPath(x, y, glyphs,
this,
1200 si.analysis.bidiLevel % 2
1201 ? QTextItem::RenderFlags(QTextItem::RightToLeft)
1202 : QTextItem::RenderFlags{});
1204 const qreal lw = fe->lineThickness().toReal();
1205 if (f.d->underline) {
1206 qreal pos = fe->underlinePosition().toReal();
1207 addRect(x, y + pos, si.width.toReal(), lw);
1209 if (f.d->overline) {
1210 qreal pos = fe->ascent().toReal() + 1;
1211 addRect(x, y - pos, si.width.toReal(), lw);
1213 if (f.d->strikeOut) {
1214 qreal pos = fe->ascent().toReal() / 3;
1215 addRect(x, y - pos, si.width.toReal(), lw);
1218 x += si.width.toReal();
1223
1224
1225
1226
1227
1228
1229
1230void QPainterPath::addPath(
const QPainterPath &other)
1232 if (other.isEmpty())
1238 QPainterPathPrivate *d = d_func();
1240 if (d->elements.constLast().type == MoveToElement)
1241 d->elements.remove(d->elements.size()-1);
1244 int cStart = d->elements.size() + other.d_func()->cStart;
1245 d->elements += other.d_func()->elements;
1248 d->require_moveTo = other.d_func()->isClosed();
1253
1254
1255
1256
1257
1258
1259
1260
1261void QPainterPath::connectPath(
const QPainterPath &other)
1263 if (other.isEmpty())
1269 QPainterPathPrivate *d = d_func();
1271 if (d->elements.constLast().type == MoveToElement)
1272 d->elements.remove(d->elements.size()-1);
1275 int cStart = d->elements.size() + other.d_func()->cStart;
1276 int first = d->elements.size();
1277 d->elements += other.d_func()->elements;
1280 d->elements[first].type = LineToElement;
1283 if (first > 0 && QPointF(d->elements.at(first)) == QPointF(d->elements.at(first - 1))) {
1284 d->elements.remove(first--);
1288 if (cStart != first)
1293
1294
1295
1296
1297
1298
1299void QPainterPath::addRegion(
const QRegion ®ion)
1304 for (
const QRect &rect : region)
1310
1311
1312
1313
1314Qt::FillRule QPainterPath::fillRule()
const
1316 return d_func() && d_func()->hasWindingFill ? Qt::WindingFill : Qt::OddEvenFill;
1320
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336void QPainterPath::setFillRule(Qt::FillRule fillRule)
1339 const bool isWindingRequested = (fillRule == Qt::WindingFill);
1340 if (d_func()->hasWindingFill == isWindingRequested)
1344 d_func()->hasWindingFill = isWindingRequested;
1347#define QT_BEZIER_A(bezier, coord) 3
* (-bezier.coord##1
1352#define QT_BEZIER_B(bezier, coord) 6
* (bezier.coord##1
1356#define QT_BEZIER_C(bezier, coord) 3
* (- bezier.coord##1
1359#define QT_BEZIER_CHECK_T(bezier, t)
1360 if (t >= 0
&& t <= 1
) {
1361 QPointF p(b.pointAt(t));
1362 if (p.x() < minx) minx = p.x();
1363 else if (p.x() > maxx) maxx = p.x();
1364 if (p.y() < miny) miny = p.y();
1365 else if (p.y() > maxy) maxy = p.y();
1371 qreal minx, miny, maxx, maxy;
1395 if (qFuzzyIsNull(ax)) {
1398 if (!qFuzzyIsNull(bx)) {
1404 const qreal tx = bx * bx - 4 * ax * cx;
1407 qreal temp = qSqrt(tx);
1408 qreal rcp = 1 / (2 * ax);
1409 qreal t1 = (-bx + temp) * rcp;
1412 qreal t2 = (-bx - temp) * rcp;
1425 if (qFuzzyIsNull(ay)) {
1428 if (!qFuzzyIsNull(by)) {
1434 const qreal ty = by * by - 4 * ay * cy;
1437 qreal temp = qSqrt(ty);
1438 qreal rcp = 1 / (2 * ay);
1439 qreal t1 = (-by + temp) * rcp;
1442 qreal t2 = (-by - temp) * rcp;
1447 return QRectF(minx, miny, maxx - minx, maxy - miny);
1451
1452
1453
1454
1455
1456QRectF QPainterPath::boundingRect()
const
1460 QPainterPathPrivate *d = d_func();
1463 computeBoundingRect();
1468
1469
1470
1471
1472
1473
1474
1475
1476
1477QRectF QPainterPath::controlPointRect()
const
1481 QPainterPathPrivate *d = d_func();
1483 if (d->dirtyControlBounds)
1484 computeControlPointRect();
1485 return d->controlBounds;
1490
1491
1492
1493
1494
1495
1496
1498bool QPainterPath::isEmpty()
const
1500 return !d_ptr || (d_ptr->elements.size() == 1 && d_ptr->elements.constFirst().type == MoveToElement);
1504
1505
1506
1507
1508
1509
1510
1511QPainterPath QPainterPath::toReversed()
const
1513 Q_D(
const QPainterPath);
1521 rev.moveTo(d->elements.at(d->elements.size()-1).x, d->elements.at(d->elements.size()-1).y);
1523 for (
int i=d->elements.size()-1; i>=1; --i) {
1524 const QPainterPath::Element &elm = d->elements.at(i);
1525 const QPainterPath::Element &prev = d->elements.at(i-1);
1528 rev.lineTo(prev.x, prev.y);
1531 rev.moveTo(prev.x, prev.y);
1533 case CurveToDataElement:
1536 const QPainterPath::Element &cp1 = d->elements.at(i-2);
1537 const QPainterPath::Element &sp = d->elements.at(i-3);
1538 Q_ASSERT(prev.type == CurveToDataElement);
1539 Q_ASSERT(cp1.type == CurveToElement);
1540 rev.cubicTo(prev.x, prev.y, cp1.x, cp1.y, sp.x, sp.y);
1545 Q_ASSERT(!
"qt_reversed_path");
1554
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565QList<QPolygonF> QPainterPath::toSubpathPolygons(
const QTransform &matrix)
const
1568 Q_D(
const QPainterPath);
1569 QList<QPolygonF> flatCurves;
1574 for (
int i=0; i<elementCount(); ++i) {
1575 const QPainterPath::Element &e = d->elements.at(i);
1577 case QPainterPath::MoveToElement:
1578 if (current.size() > 1)
1579 flatCurves += current;
1581 current.reserve(16);
1582 current += QPointF(e.x, e.y) * matrix;
1584 case QPainterPath::LineToElement:
1585 current += QPointF(e.x, e.y) * matrix;
1587 case QPainterPath::CurveToElement: {
1588 Q_ASSERT(d->elements.at(i+1).type == QPainterPath::CurveToDataElement);
1589 Q_ASSERT(d->elements.at(i+2).type == QPainterPath::CurveToDataElement);
1590 QBezier bezier = QBezier::fromPoints(QPointF(d->elements.at(i-1).x, d->elements.at(i-1).y) * matrix,
1591 QPointF(e.x, e.y) * matrix,
1592 QPointF(d->elements.at(i+1).x, d->elements.at(i+1).y) * matrix,
1593 QPointF(d->elements.at(i+2).x, d->elements.at(i+2).y) * matrix);
1594 bezier.addToPolygon(¤t);
1598 case QPainterPath::CurveToDataElement:
1599 Q_ASSERT(!
"QPainterPath::toSubpathPolygons(), bad element type");
1604 if (current.size()>1)
1605 flatCurves += current;
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632QList<QPolygonF> QPainterPath::toFillPolygons(
const QTransform &matrix)
const
1635 QList<QPolygonF> polys;
1637 QList<QPolygonF> subpaths = toSubpathPolygons(matrix);
1638 int count = subpaths.size();
1643 QList<QRectF> bounds;
1644 bounds.reserve(count);
1645 for (
int i=0; i<count; ++i)
1646 bounds += subpaths.at(i).boundingRect();
1648#ifdef QPP_FILLPOLYGONS_DEBUG
1649 printf(
"QPainterPath::toFillPolygons, subpathCount=%d\n", count);
1650 for (
int i=0; i<bounds.size(); ++i)
1651 qDebug() <<
" bounds" << i << bounds.at(i);
1654 QList< QList<
int> > isects;
1655 isects.resize(count);
1658 for (
int j=0; j<count; ++j) {
1659 if (subpaths.at(j).size() <= 2)
1661 QRectF cbounds = bounds.at(j);
1662 for (
int i=0; i<count; ++i) {
1663 if (cbounds.intersects(bounds.at(i))) {
1669#ifdef QPP_FILLPOLYGONS_DEBUG
1670 printf(
"Intersections before flattening:\n");
1671 for (
int i = 0; i < count; ++i) {
1673 for (
int j = 0; j < isects[i].size(); ++j) {
1674 printf(
"%d ", isects[i][j]);
1681 for (
int i=0; i<count; ++i) {
1682 const QList<
int> ¤t_isects = isects.at(i);
1683 for (
int j=0; j<current_isects.size(); ++j) {
1684 int isect_j = current_isects.at(j);
1687 const QList<
int> &isects_j = isects.at(isect_j);
1688 for (
int k = 0, size = isects_j.size(); k < size; ++k) {
1689 int isect_k = isects_j.at(k);
1690 if (isect_k != i && !isects.at(i).contains(isect_k)) {
1691 isects[i] += isect_k;
1694 isects[isect_j].clear();
1698#ifdef QPP_FILLPOLYGONS_DEBUG
1699 printf(
"Intersections after flattening:\n");
1700 for (
int i = 0; i < count; ++i) {
1702 for (
int j = 0; j < isects[i].size(); ++j) {
1703 printf(
"%d ", isects[i][j]);
1710 for (
int i=0; i<count; ++i) {
1711 const QList<
int> &subpath_list = isects.at(i);
1712 if (!subpath_list.isEmpty()) {
1714 for (
int j=0; j<subpath_list.size(); ++j) {
1715 const QPolygonF &subpath = subpaths.at(subpath_list.at(j));
1717 if (!subpath.isClosed())
1718 buildUp += subpath.first();
1719 if (!buildUp.isClosed())
1720 buildUp += buildUp.constFirst();
1743 if (qFuzzyCompare(y1, y2)) {
1746 }
else if (y2 < y1) {
1747 qreal x_tmp = x2; x2 = x1; x1 = x_tmp;
1748 qreal y_tmp = y2; y2 = y1; y1 = y_tmp;
1752 if (y >= y1 && y < y2) {
1753 qreal x = x1 + ((x2 - x1) / (y2 - y1)) * (y - y1);
1763 int *winding,
int depth = 0)
1767 QRectF bounds = bezier.bounds();
1773 if (y >= bounds.y() && y < bounds.y() + bounds.height()) {
1777 const qreal lower_bound = qreal(.001);
1778 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound)) {
1782 if (bezier.pt1().x() <= x) {
1783 (*winding) += (bezier.pt4().y() > bezier.pt1().y() ? 1 : -1);
1789 const auto halves = bezier.split();
1790 qt_painterpath_isect_curve(halves.first, pt, winding, depth + 1);
1791 qt_painterpath_isect_curve(halves.second, pt, winding, depth + 1);
1796
1797
1798
1799
1800
1801
1802
1803bool QPainterPath::contains(
const QPointF &pt)
const
1805 if (isEmpty() || !controlPointRect().contains(pt))
1808 QPainterPathPrivate *d = d_func();
1810 int winding_number = 0;
1814 for (
int i=0; i<d->elements.size(); ++i) {
1815 const Element &e = d->elements.at(i);
1821 qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number);
1822 last_start = last_pt = e;
1826 qt_painterpath_isect_line(last_pt, e, pt, &winding_number);
1830 case CurveToElement:
1832 const QPainterPath::Element &cp2 = d->elements.at(++i);
1833 const QPainterPath::Element &ep = d->elements.at(++i);
1834 qt_painterpath_isect_curve(QBezier::fromPoints(last_pt, e, cp2, ep),
1835 pt, &winding_number);
1847 if (last_pt != last_start)
1848 qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number);
1850 return (d->hasWindingFill
1851 ? (winding_number != 0)
1852 : ((winding_number % 2) != 0));
1860 qreal left = rect.left();
1861 qreal right = rect.right();
1862 qreal top = rect.top();
1863 qreal bottom = rect.bottom();
1866 int p1 = ((x1 < left) <<
Left)
1867 | ((x1 > right) <<
Right)
1868 | ((y1 < top) <<
Top)
1869 | ((y1 > bottom) <<
Bottom);
1870 int p2 = ((x2 < left) <<
Left)
1871 | ((x2 > right) <<
Right)
1872 | ((y2 < top) <<
Top)
1873 | ((y2 > bottom) <<
Bottom);
1885 y1 += dy/dx * (left - x1);
1887 }
else if (x1 > right) {
1888 y1 -= dy/dx * (x1 - right);
1892 y2 += dy/dx * (left - x2);
1894 }
else if (x2 > right) {
1895 y2 -= dy/dx * (x2 - right);
1899 p1 = ((y1 < top) <<
Top)
1900 | ((y1 > bottom) <<
Bottom);
1901 p2 = ((y2 < top) <<
Top)
1902 | ((y2 > bottom) <<
Bottom);
1909 x1 += dx/dy * (top - y1);
1911 }
else if (y1 > bottom) {
1912 x1 -= dx/dy * (y1 - bottom);
1916 x2 += dx/dy * (top - y2);
1918 }
else if (y2 > bottom) {
1919 x2 -= dx/dy * (y2 - bottom);
1923 p1 = ((x1 < left) <<
Left)
1924 | ((x1 > right) <<
Right);
1925 p2 = ((x2 < left) <<
Left)
1926 | ((x2 > right) <<
Right);
1938 QRectF bounds = bezier.bounds();
1940 if (y >= bounds.top() && y < bounds.bottom()
1941 && bounds.right() >= x1 && bounds.left() < x2) {
1942 const qreal lower_bound = qreal(.01);
1943 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound))
1946 const auto halves = bezier.split();
1947 if (qt_isect_curve_horizontal(halves.first, y, x1, x2, depth + 1)
1948 || qt_isect_curve_horizontal(halves.second, y, x1, x2, depth + 1))
1956 QRectF bounds = bezier.bounds();
1958 if (x >= bounds.left() && x < bounds.right()
1959 && bounds.bottom() >= y1 && bounds.top() < y2) {
1960 const qreal lower_bound = qreal(.01);
1961 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound))
1964 const auto halves = bezier.split();
1965 if (qt_isect_curve_vertical(halves.first, x, y1, y2, depth + 1)
1966 || qt_isect_curve_vertical(halves.second, x, y1, y2, depth + 1))
1974 if ((point.x() == rect.left() || point.x() == rect.right()) &&
1975 (point.y() >= rect.top() && point.y() <= rect.bottom()))
1977 if ((point.y() == rect.top() || point.y() == rect.bottom()) &&
1978 (point.x() >= rect.left() && point.x() <= rect.right()))
1984
1985
1990 enum { OnRect, InsideRect, OutsideRect} edgeStatus = OnRect;
1991 for (
int i=0; i<path->elementCount(); ++i) {
1992 const QPainterPath::Element &e = path->elementAt(i);
1996 case QPainterPath::MoveToElement:
1998 && qFuzzyCompare(last_pt.x(), last_start.x())
1999 && qFuzzyCompare(last_pt.y(), last_start.y())
2000 && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
2001 last_start.x(), last_start.y(), rect))
2003 last_start = last_pt = e;
2006 case QPainterPath::LineToElement:
2007 if (qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(), e.x, e.y, rect))
2012 case QPainterPath::CurveToElement:
2014 QPointF cp2 = path->elementAt(++i);
2015 QPointF ep = path->elementAt(++i);
2016 QBezier bezier = QBezier::fromPoints(last_pt, e, cp2, ep);
2017 if (qt_isect_curve_horizontal(bezier, rect.top(), rect.left(), rect.right())
2018 || qt_isect_curve_horizontal(bezier, rect.bottom(), rect.left(), rect.right())
2019 || qt_isect_curve_vertical(bezier, rect.left(), rect.top(), rect.bottom())
2020 || qt_isect_curve_vertical(bezier, rect.right(), rect.top(), rect.bottom()))
2031 if (!pointOnEdge(rect, last_pt)) {
2032 bool contained = rect.contains(last_pt);
2033 switch (edgeStatus) {
2043 edgeStatus = contained ? InsideRect : OutsideRect;
2047 if (last_pt == last_start)
2048 edgeStatus = OnRect;
2053 if (last_pt != last_start
2054 && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
2055 last_start.x(), last_start.y(), rect))
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074
2075bool QPainterPath::intersects(
const QRectF &rect)
const
2077 if (elementCount() == 1 && rect.contains(elementAt(0)))
2083 QRectF cp = controlPointRect();
2084 QRectF rn = rect.normalized();
2089 if (qMax(rn.left(), cp.left()) > qMin(rn.right(), cp.right())
2090 || qMax(rn.top(), cp.top()) > qMin(rn.bottom(), cp.bottom()))
2094 if (qt_painterpath_check_crossing(
this, rect))
2097 if (contains(rect.center()))
2103 for (
int i=0; i<d->elements.size(); ++i) {
2104 const Element &e = d->elements.at(i);
2105 if (e.type == QPainterPath::MoveToElement && rect.contains(e))
2113
2114
2115
2116
2117
2118void QPainterPath::translate(qreal dx, qreal dy)
2120 if (!d_ptr || (dx == 0 && dy == 0))
2123 int elementsLeft = d_ptr->elements.size();
2124 if (elementsLeft <= 0)
2128 QPainterPath::Element *element = d_func()->elements.data();
2130 while (elementsLeft--) {
2138
2139
2140
2141
2142
2143
2144
2145
2148
2149
2150
2151
2152
2153QPainterPath QPainterPath::translated(qreal dx, qreal dy)
const
2155 QPainterPath copy(*
this);
2156 copy.translate(dx, dy);
2161
2162
2163
2164
2165
2166
2167
2168
2171
2172
2173
2174
2175
2176bool QPainterPath::contains(
const QRectF &rect)
const
2182 if (isEmpty() || !controlPointRect().contains(rect))
2188 if (qt_painterpath_check_crossing(
this, rect)) {
2189 if (fillRule() == Qt::OddEvenFill) {
2194 if (!contains(rect.topLeft()) ||
2195 !contains(rect.topRight()) ||
2196 !contains(rect.bottomRight()) ||
2197 !contains(rect.bottomLeft()))
2206 if (!contains(rect.center()))
2215 for (
int i=0; i<d->elements.size(); ++i) {
2216 const Element &e = d->elements.at(i);
2217 if (e.type == QPainterPath::MoveToElement && rect.contains(e)) {
2218 if (fillRule() == Qt::OddEvenFill)
2222 for (; !stop && i<d->elements.size(); ++i) {
2223 const Element &el = d->elements.at(i);
2232 case CurveToElement:
2233 if (!contains(d->elements.at(i+2)))
2250static inline bool epsilonCompare(
const QPointF &a,
const QPointF &b,
const QSizeF &epsilon)
2252 return qAbs(a.x() - b.x()) <= epsilon.width()
2253 && qAbs(a.y() - b.y()) <= epsilon.height();
2257
2258
2259
2260
2261
2262
2263
2265bool QPainterPath::operator==(
const QPainterPath &path)
const
2267 QPainterPathPrivate *d = d_func();
2268 QPainterPathPrivate *other_d = path.d_func();
2271 }
else if (!d || !other_d) {
2272 if (!other_d && isEmpty() && elementAt(0) == QPointF() && !d->hasWindingFill)
2274 if (!d && path.isEmpty() && path.elementAt(0) == QPointF() && !other_d->hasWindingFill)
2278 else if (d->hasWindingFill != other_d->hasWindingFill)
2280 else if (d->elements.size() != other_d->elements.size())
2283 const qreal qt_epsilon =
sizeof(qreal) ==
sizeof(
double) ? 1e-12 : qreal(1e-5);
2285 QSizeF epsilon = boundingRect().size();
2286 epsilon.rwidth() *= qt_epsilon;
2287 epsilon.rheight() *= qt_epsilon;
2289 for (
int i = 0; i < d->elements.size(); ++i)
2290 if (d->elements.at(i).type != other_d->elements.at(i).type
2291 || !epsilonCompare(d->elements.at(i), other_d->elements.at(i), epsilon))
2298
2299
2300
2301
2302
2303
2304
2306bool QPainterPath::operator!=(
const QPainterPath &path)
const
2308 return !(*
this==path);
2312
2313
2314
2315
2316
2317
2318QPainterPath QPainterPath::operator&(
const QPainterPath &other)
const
2320 return intersected(other);
2324
2325
2326
2327
2328
2329
2330QPainterPath QPainterPath::operator|(
const QPainterPath &other)
const
2332 return united(other);
2336
2337
2338
2339
2340
2341
2342
2343QPainterPath QPainterPath::operator+(
const QPainterPath &other)
const
2345 return united(other);
2349
2350
2351
2352
2353
2354
2355QPainterPath QPainterPath::operator-(
const QPainterPath &other)
const
2357 return subtracted(other);
2361
2362
2363
2364
2365
2366
2367QPainterPath &QPainterPath::operator&=(
const QPainterPath &other)
2369 return *
this = (*
this & other);
2373
2374
2375
2376
2377
2378
2379QPainterPath &QPainterPath::operator|=(
const QPainterPath &other)
2381 return *
this = (*
this | other);
2385
2386
2387
2388
2389
2390
2391
2392QPainterPath &QPainterPath::operator+=(
const QPainterPath &other)
2394 return *
this = (*
this + other);
2398
2399
2400
2401
2402
2403
2404
2405QPainterPath &QPainterPath::operator-=(
const QPainterPath &other)
2407 return *
this = (*
this - other);
2410#ifndef QT_NO_DATASTREAM
2412
2413
2414
2415
2416
2417
2418
2419
2427 s << p.elementCount();
2428 for (
int i=0; i < p.d_func()->elements.size(); ++i) {
2429 const QPainterPath::Element &e = p.d_func()->elements.at(i);
2431 s <<
double(e.x) <<
double(e.y);
2433 s << p.d_func()->cStart;
2434 s <<
int(p.fillRule());
2439
2440
2441
2442
2443
2444
2445
2446
2449 bool errorDetected =
false;
2460 p.d_func()->elements.clear();
2461 for (
int i=0; i<size; ++i) {
2467 Q_ASSERT(type >= 0 && type <= 3);
2468 if (!isValidCoord(qreal(x)) || !isValidCoord(qreal(y))) {
2470 qWarning(
"QDataStream::operator>>: Invalid QPainterPath coordinates read, skipping it");
2472 errorDetected =
true;
2475 QPainterPath::Element elm = { qreal(x), qreal(y), QPainterPath::ElementType(type) };
2476 p.d_func()->elements.append(elm);
2478 s >> p.d_func()->cStart;
2481 Q_ASSERT(fillRule == Qt::OddEvenFill || fillRule == Qt::WindingFill);
2482 p.d_func()->hasWindingFill = (Qt::FillRule(fillRule) == Qt::WindingFill);
2483 if (errorDetected || p.d_func()->elements.isEmpty())
2491
2492
2496 ((QPainterPath *) data)->moveTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
2501 ((QPainterPath *) data)->lineTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
2505 qfixed c2x, qfixed c2y,
2506 qfixed ex, qfixed ey,
2509 ((QPainterPath *) data)->cubicTo(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y),
2510 qt_fixed_to_real(c2x), qt_fixed_to_real(c2y),
2511 qt_fixed_to_real(ex), qt_fixed_to_real(ey));
2515
2516
2517
2518
2519
2520
2521
2522
2523
2524
2525
2526
2527
2528
2529
2530
2531
2532
2533
2534
2535
2536
2537
2538
2539
2540
2541
2542
2543
2544
2545
2546
2547
2548
2549
2550
2551
2552
2553
2554
2555
2556
2557
2558
2559
2560
2561
2562
2563
2568 stroker.setMoveToHook(qt_path_stroke_move_to);
2569 stroker.setLineToHook(qt_path_stroke_line_to);
2570 stroker.setCubicToHook(qt_path_stroke_cubic_to);
2574
2575
2576QPainterPathStroker::QPainterPathStroker()
2577 : d_ptr(
new QPainterPathStrokerPrivate)
2582
2583
2584
2585
2586QPainterPathStroker::QPainterPathStroker(
const QPen &pen)
2587 : d_ptr(
new QPainterPathStrokerPrivate)
2589 setWidth(pen.widthF());
2590 setCapStyle(pen.capStyle());
2591 setJoinStyle(pen.joinStyle());
2592 setMiterLimit(pen.miterLimit());
2593 setDashOffset(pen.dashOffset());
2595 if (pen.style() == Qt::CustomDashLine)
2596 setDashPattern(pen.dashPattern());
2598 setDashPattern(pen.style());
2602
2603
2604QPainterPathStroker::~QPainterPathStroker()
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621
2622QPainterPath QPainterPathStroker::createStroke(
const QPainterPath &path)
const
2624 QPainterPathStrokerPrivate *d =
const_cast<QPainterPathStrokerPrivate *>(d_func());
2625 QPainterPath stroke;
2628 if (d->dashPattern.isEmpty()) {
2629 d->stroker.strokePath(path, &stroke, QTransform());
2631 QDashStroker dashStroker(&d->stroker);
2632 dashStroker.setDashPattern(d->dashPattern);
2633 dashStroker.setDashOffset(d->dashOffset);
2634 dashStroker.setClipRect(d->stroker.clipRect());
2635 dashStroker.strokePath(path, &stroke, QTransform());
2637 stroke.setFillRule(Qt::WindingFill);
2642
2643
2644
2645
2646
2647void QPainterPathStroker::setWidth(qreal width)
2649 Q_D(QPainterPathStroker);
2652 d->stroker.setStrokeWidth(qt_real_to_fixed(width));
2656
2657
2658qreal QPainterPathStroker::width()
const
2660 return qt_fixed_to_real(d_func()->stroker.strokeWidth());
2665
2666
2667
2668
2669void QPainterPathStroker::setCapStyle(Qt::PenCapStyle style)
2671 d_func()->stroker.setCapStyle(style);
2676
2677
2678Qt::PenCapStyle QPainterPathStroker::capStyle()
const
2680 return d_func()->stroker.capStyle();
2684
2685
2686void QPainterPathStroker::setJoinStyle(Qt::PenJoinStyle style)
2688 d_func()->stroker.setJoinStyle(style);
2692
2693
2694Qt::PenJoinStyle QPainterPathStroker::joinStyle()
const
2696 return d_func()->stroker.joinStyle();
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709void QPainterPathStroker::setMiterLimit(qreal limit)
2711 d_func()->stroker.setMiterLimit(qt_real_to_fixed(limit));
2715
2716
2717qreal QPainterPathStroker::miterLimit()
const
2719 return qt_fixed_to_real(d_func()->stroker.miterLimit());
2724
2725
2726
2727
2728
2729
2730
2731void QPainterPathStroker::setCurveThreshold(qreal threshold)
2733 d_func()->stroker.setCurveThreshold(qt_real_to_fixed(threshold));
2737
2738
2739
2740qreal QPainterPathStroker::curveThreshold()
const
2742 return qt_fixed_to_real(d_func()->stroker.curveThreshold());
2746
2747
2748void QPainterPathStroker::setDashPattern(Qt::PenStyle style)
2750 d_func()->dashPattern = QDashStroker::patternForStyle(style);
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
2769void QPainterPathStroker::setDashPattern(
const QList<qreal> &dashPattern)
2771 d_func()->dashPattern.clear();
2772 for (
int i=0; i<dashPattern.size(); ++i)
2773 d_func()->dashPattern << qt_real_to_fixed(dashPattern.at(i));
2777
2778
2779QList<qreal> QPainterPathStroker::dashPattern()
const
2781 return d_func()->dashPattern;
2785
2786
2787qreal QPainterPathStroker::dashOffset()
const
2789 return d_func()->dashOffset;
2793
2794
2795
2796
2797
2798void QPainterPathStroker::setDashOffset(qreal offset)
2800 d_func()->dashOffset = offset;
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817
2818QPolygonF QPainterPath::toFillPolygon(
const QTransform &matrix)
const
2820 const QList<QPolygonF> flats = toSubpathPolygons(matrix);
2822 if (flats.isEmpty())
2824 QPointF first = flats.first().first();
2825 for (
int i=0; i<flats.size(); ++i) {
2826 polygon += flats.at(i);
2827 if (!flats.at(i).isClosed())
2828 polygon += flats.at(i).first();
2836
2837
2838
2839
2840
2841bool QPainterPath::isCachingEnabled()
const
2844 return d && d->cacheEnabled;
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858
2859void QPainterPath::setCachingEnabled(
bool enabled)
2862 if (d_func()->cacheEnabled == enabled)
2865 QPainterPathPrivate *d = d_func();
2866 d->cacheEnabled = enabled;
2868 d->m_runLengths.clear();
2869 d->m_runLengths.squeeze();
2876 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
2880
2881
2882qreal QPainterPath::length()
const
2887 if (d->cacheEnabled) {
2888 if (d->dirtyRunLengths)
2889 d->computeRunLengths();
2890 return d->m_runLengths.last();
2894 for (
int i=1; i<d->elements.size(); ++i) {
2895 const Element &e = d->elements.at(i);
2902 len += QLineF(d->elements.at(i-1), e).length();
2905 case CurveToElement:
2907 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
2909 d->elements.at(i+1),
2910 d->elements.at(i+2));
2923
2924
2925
2926
2927
2928
2929
2930qreal QPainterPath::percentAtLength(qreal len)
const
2933 if (isEmpty() || len <= 0)
2936 qreal totalLength = length();
2937 if (len > totalLength)
2940 if (d->cacheEnabled) {
2941 const int ei = qMax(d->elementAtT(len / totalLength), 1);
2943 const QPainterPath::Element &e = d->elements[ei];
2945 case QPainterPath::LineToElement:
2946 res = len / totalLength;
2948 case CurveToElement:
2950 QBezier b = QBezier::fromPoints(d->elements.at(ei-1),
2952 d->elements.at(ei+1),
2953 d->elements.at(ei+2));
2954 qreal prevLen = d->m_runLengths[ei - 1];
2955 qreal blen = d->m_runLengths[ei] - prevLen;
2956 qreal elemRes = b.tAtLength(len - prevLen);
2957 res = (elemRes * blen + prevLen) / totalLength;
2967 for (
int i=1; i<d->elements.size(); ++i) {
2968 const Element &e = d->elements.at(i);
2975 QLineF line(d->elements.at(i-1), e);
2976 qreal llen = line.length();
2978 if (curLen >= len) {
2979 return len/totalLength ;
2984 case CurveToElement:
2986 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
2988 d->elements.at(i+1),
2989 d->elements.at(i+2));
2990 qreal blen = b.length();
2991 qreal prevLen = curLen;
2994 if (curLen >= len) {
2995 qreal res = b.tAtLength(len - prevLen);
2996 return (res * blen + prevLen)/totalLength;
3011 qreal *bezierLength)
3013 *startingLength = 0;
3018 qreal totalLength = path.length();
3020 const int lastElement = path.elementCount() - 1;
3021 for (
int i=0; i <= lastElement; ++i) {
3022 const QPainterPath::Element &e = path.elementAt(i);
3025 case QPainterPath::MoveToElement:
3027 case QPainterPath::LineToElement:
3029 QLineF line(path.elementAt(i-1), e);
3030 qreal llen = line.length();
3032 if (i == lastElement || curLen/totalLength >= t) {
3033 *bezierLength = llen;
3034 QPointF a = path.elementAt(i-1);
3035 QPointF delta = e - a;
3036 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
3040 case QPainterPath::CurveToElement:
3042 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
3044 path.elementAt(i+1),
3045 path.elementAt(i+2));
3046 qreal blen = b.length();
3049 if (i + 2 == lastElement || curLen/totalLength >= t) {
3050 *bezierLength = blen;
3060 *startingLength = curLen;
3066 qreal *bezierLength)
const
3068 Q_ASSERT(t >= 0 && t <= 1);
3070 if (!path.isEmpty() && d->cacheEnabled) {
3071 const int ei = qMax(d->elementAtT(t), 1);
3072 const qreal prevRunLength = d->m_runLengths[ei - 1];
3073 *startingLength = prevRunLength;
3074 *bezierLength = d->m_runLengths[ei] - prevRunLength;
3075 const QPointF prev = d->elements[ei - 1];
3076 const QPainterPath::Element &e = d->elements[ei];
3078 case QPainterPath::LineToElement:
3080 QPointF delta = (e - prev) / 3;
3081 return QBezier::fromPoints(prev, prev + delta, prev + 2 * delta, e);
3083 case QPainterPath::CurveToElement:
3084 return QBezier::fromPoints(prev, e, elements[ei + 1], elements[ei + 2]);
3091 return uncached_bezierAtT(path, t, startingLength, bezierLength);
3095
3096
3097
3098
3099
3100
3101
3102
3103QPointF QPainterPath::pointAtPercent(qreal t)
const
3105 if (t < 0 || t > 1) {
3106 qWarning(
"QPainterPath::pointAtPercent accepts only values between 0 and 1");
3110 if (!d_ptr || d_ptr->elements.size() == 0)
3113 if (d_ptr->elements.size() == 1)
3114 return d_ptr->elements.at(0);
3116 qreal totalLength = length();
3118 qreal bezierLen = 0;
3119 QBezier b = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3120 qreal realT = (totalLength * t - curLen) / bezierLen;
3122 return b.pointAt(qBound(qreal(0), realT, qreal(1)));
3126
3127
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137qreal QPainterPath::angleAtPercent(qreal t)
const
3139 if (t < 0 || t > 1) {
3140 qWarning(
"QPainterPath::angleAtPercent accepts only values between 0 and 1");
3144 qreal totalLength = length();
3146 qreal bezierLen = 0;
3147 QBezier bez = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3148 qreal realT = (totalLength * t - curLen) / bezierLen;
3150 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3151 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3153 return QLineF(0, 0, m1, m2).angle();
3158
3159
3160
3161
3162
3163
3164
3165
3166qreal QPainterPath::slopeAtPercent(qreal t)
const
3168 if (t < 0 || t > 1) {
3169 qWarning(
"QPainterPath::slopeAtPercent accepts only values between 0 and 1");
3173 qreal totalLength = length();
3175 qreal bezierLen = 0;
3176 QBezier bez = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3177 qreal realT = (totalLength * t - curLen) / bezierLen;
3179 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3180 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3187 if (std::numeric_limits<qreal>::has_infinity) {
3188 slope = (m2 < 0) ? -std::numeric_limits<qreal>::infinity()
3189 : std::numeric_limits<qreal>::infinity();
3191 if (
sizeof(qreal) ==
sizeof(
double)) {
3192 return 1.79769313486231570e+308;
3194 return ((qreal)3.40282346638528860e+38);
3203
3204
3205
3206
3207
3208
3209
3210
3211
3212
3213
3214
3215
3216
3217
3219QPainterPath QPainterPath::trimmed(qreal fromFraction, qreal toFraction, qreal offset)
const
3225 if (!isCachingEnabled()) {
3226 QPainterPath copy(*
this);
3227 copy.setCachingEnabled(
true);
3228 return copy.trimmed(fromFraction, toFraction, offset);
3231 qreal f1 = qBound(qreal(0), fromFraction, qreal(1));
3232 qreal f2 = qBound(qreal(0), toFraction, qreal(1));
3235 if (qFuzzyCompare(f2 - f1, qreal(1)))
3239 if (qFuzzyCompare(f1, f2))
3241 res.setFillRule(fillRule());
3245 offset = std::modf(offset, &dummy);
3247 qreal of1 = f1 + offset;
3248 qreal of2 = f2 + offset;
3250 f1 = of1 < 0 ? of1 + 1 : of1;
3251 f2 = of2 + 1 > 1 ? of2 : of2 + 1;
3252 }
else if (offset > 0) {
3253 f1 = of1 - 1 < 0 ? of1 : of1 - 1;
3254 f2 = of2 > 1 ? of2 - 1 : of2;
3257 const bool wrapping = (f1 > f2);
3260 QPainterPathPrivate *d = d_func();
3261 if (d->dirtyRunLengths)
3262 d->computeRunLengths();
3263 const qreal totalLength = d->m_runLengths.last();
3264 if (qFuzzyIsNull(totalLength))
3267 const qreal l1 = f1 * totalLength;
3268 const qreal l2 = f2 * totalLength;
3269 const int e1 = d->elementAtLength(l1);
3270 const bool mustTrimE1 = !qFuzzyCompare(d->m_runLengths.at(e1), l1);
3271 const int e2 = d->elementAtLength(l2);
3272 const bool mustTrimE2 = !qFuzzyCompare(d->m_runLengths.at(e2), l2);
3275 if (e1 == e2 && !wrapping && mustTrimE1 && mustTrimE2) {
3277 d->appendSliceOfElement(&res, e1, l1, l2);
3281 d->appendEndOfElement(&res, e1, l1);
3283 res.moveTo(d->endPointOfElement(e1));
3286 int firstWholeElement = e1 + 1;
3287 int lastWholeElement = (mustTrimE2 ? e2 - 1 : e2);
3289 d->appendElementRange(&res, firstWholeElement, lastWholeElement);
3291 int lastIndex = d->elements.size() - 1;
3292 d->appendElementRange(&res, firstWholeElement, lastIndex);
3293 bool isClosed = (QPointF(d->elements.at(0)) == QPointF(d->elements.at(lastIndex)));
3295 d->appendElementRange(&res, (isClosed ? 1 : 0), lastWholeElement);
3300 d->appendStartOfElement(&res, e2, l2);
3307 qreal startLen, qreal endLen)
3309 Q_ASSERT(cacheEnabled);
3310 Q_ASSERT(!dirtyRunLengths);
3312 if (elemIdx <= 0 || elemIdx >= elements.size())
3315 const qreal prevLen = m_runLengths.at(elemIdx - 1);
3316 const qreal elemLen = m_runLengths.at(elemIdx) - prevLen;
3317 const qreal len1 = startLen - prevLen;
3318 const qreal len2 = endLen - prevLen;
3319 if (qFuzzyIsNull(elemLen))
3322 const QPointF pp = elements.at(elemIdx - 1);
3323 const QPainterPath::Element e = elements.at(elemIdx);
3326 QPointF p1 = (trimFlags & TrimStart) ? l.pointAt(len1 / elemLen) : pp;
3327 QPointF p2 = (trimFlags & TrimEnd) ? l.pointAt(len2 / elemLen) : e;
3331 }
else if (e.isCurveTo()) {
3332 Q_ASSERT(elemIdx < elements.size() - 2);
3333 QBezier b = QBezier::fromPoints(pp, e, elements.at(elemIdx + 1), elements.at(elemIdx + 2));
3334 qreal t1 = (trimFlags & TrimStart) ? b.tAtLength(len1) : 0.0;
3335 qreal t2 = (trimFlags & TrimEnd) ? b.tAtLength(len2) : 1.0;
3336 QBezier c = b.getSubRange(t1, t2);
3338 to->moveTo(c.pt1());
3339 to->cubicTo(c.pt2(), c.pt3(), c.pt4());
3347 if (first < 0 || first >= elements.size() || last < 0 || last >= elements.size())
3351 for (
int i = first; i <= last; i++) {
3352 const QPainterPath::Element &e = elements.at(i);
3354 case QPainterPath::MoveToElement:
3357 case QPainterPath::LineToElement:
3360 case QPainterPath::CurveToElement:
3361 Q_ASSERT(i < elements.size() - 2);
3362 to->cubicTo(e, elements.at(i + 1), elements.at(i + 2));
3374
3375
3376
3377
3378
3379
3380
3381
3382
3383
3384
3385
3386void QPainterPath::addRoundedRect(
const QRectF &rect, qreal xRadius, qreal yRadius,
3389 QRectF r = rect.normalized();
3394 if (mode == Qt::AbsoluteSize) {
3395 qreal w = r.width() / 2;
3396 qreal h = r.height() / 2;
3401 xRadius = 100 * qMin(xRadius, w) / w;
3406 yRadius = 100 * qMin(yRadius, h) / h;
3416 if (xRadius <= 0 || yRadius <= 0) {
3423 qreal w = r.width();
3424 qreal h = r.height();
3425 qreal rxx2 = w*xRadius/100;
3426 qreal ryy2 = h*yRadius/100;
3431 bool first = d_func()->elements.size() < 2;
3433 arcMoveTo(x, y, rxx2, ryy2, 180);
3434 arcTo(x, y, rxx2, ryy2, 180, -90);
3435 arcTo(x+w-rxx2, y, rxx2, ryy2, 90, -90);
3436 arcTo(x+w-rxx2, y+h-ryy2, rxx2, ryy2, 0, -90);
3437 arcTo(x, y+h-ryy2, rxx2, ryy2, 270, -90);
3440 d_func()->require_moveTo =
true;
3441 d_func()->convex = first;
3445
3446
3447
3448
3449
3450
3453
3454
3455
3456
3457
3458
3459
3460
3461
3462
3463
3464QPainterPath QPainterPath::united(
const QPainterPath &p)
const
3466 if (isEmpty() || p.isEmpty())
3467 return isEmpty() ? p : *
this;
3468 QPathClipper clipper(*
this, p);
3469 return clipper.clip(QPathClipper::BoolOr);
3473
3474
3475
3476
3477
3478
3479QPainterPath QPainterPath::intersected(
const QPainterPath &p)
const
3481 if (isEmpty() || p.isEmpty())
3482 return QPainterPath();
3483 QPathClipper clipper(*
this, p);
3484 return clipper.clip(QPathClipper::BoolAnd);
3488
3489
3490
3491
3492
3493
3494
3495
3496
3497QPainterPath QPainterPath::subtracted(
const QPainterPath &p)
const
3499 if (isEmpty() || p.isEmpty())
3501 QPathClipper clipper(*
this, p);
3502 return clipper.clip(QPathClipper::BoolSub);
3506
3507
3508
3509
3510
3511
3512
3513
3514QPainterPath QPainterPath::simplified()
const
3518 QPathClipper clipper(*
this, QPainterPath());
3519 return clipper.clip(QPathClipper::Simplify);
3523
3524
3525
3526
3527
3528
3529
3530
3531
3532
3533bool QPainterPath::intersects(
const QPainterPath &p)
const
3535 if (p.elementCount() == 1)
3536 return contains(p.elementAt(0));
3537 if (isEmpty() || p.isEmpty())
3539 QPathClipper clipper(*
this, p);
3540 return clipper.intersect();
3544
3545
3546
3547
3548
3549
3550
3551
3552
3553
3554
3555bool QPainterPath::contains(
const QPainterPath &p)
const
3557 if (p.elementCount() == 1)
3558 return contains(p.elementAt(0));
3559 if (isEmpty() || p.isEmpty())
3561 QPathClipper clipper(*
this, p);
3562 return clipper.contains();
3565void QPainterPath::setDirty(
bool dirty)
3567 d_func()->pathConverter.reset();
3568 d_func()->dirtyBounds = dirty;
3569 d_func()->dirtyControlBounds = dirty;
3570 d_func()->dirtyRunLengths = dirty;
3571 d_func()->convex =
false;
3574void QPainterPath::computeBoundingRect()
const
3576 QPainterPathPrivate *d = d_func();
3577 d->dirtyBounds =
false;
3579 d->bounds = QRect();
3583 qreal minx, maxx, miny, maxy;
3584 minx = maxx = d->elements.at(0).x;
3585 miny = maxy = d->elements.at(0).y;
3586 for (
int i=1; i<d->elements.size(); ++i) {
3587 const Element &e = d->elements.at(i);
3592 if (e.x > maxx) maxx = e.x;
3593 else if (e.x < minx) minx = e.x;
3594 if (e.y > maxy) maxy = e.y;
3595 else if (e.y < miny) miny = e.y;
3597 case CurveToElement:
3599 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
3601 d->elements.at(i+1),
3602 d->elements.at(i+2));
3603 QRectF r = qt_painterpath_bezier_extrema(b);
3604 qreal right = r.right();
3605 qreal bottom = r.bottom();
3606 if (r.x() < minx) minx = r.x();
3607 if (right > maxx) maxx = right;
3608 if (r.y() < miny) miny = r.y();
3609 if (bottom > maxy) maxy = bottom;
3617 d->bounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3621void QPainterPath::computeControlPointRect()
const
3623 QPainterPathPrivate *d = d_func();
3624 d->dirtyControlBounds =
false;
3626 d->controlBounds = QRect();
3630 qreal minx, maxx, miny, maxy;
3631 minx = maxx = d->elements.at(0).x;
3632 miny = maxy = d->elements.at(0).y;
3633 for (
int i=1; i<d->elements.size(); ++i) {
3634 const Element &e = d->elements.at(i);
3635 if (e.x > maxx) maxx = e.x;
3636 else if (e.x < minx) minx = e.x;
3637 if (e.y > maxy) maxy = e.y;
3638 else if (e.y < miny) miny = e.y;
3640 d->controlBounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3645 Q_ASSERT(!elements.isEmpty());
3647 m_runLengths.clear();
3648 const int numElems = elements.size();
3649 m_runLengths.reserve(numElems);
3651 QPointF runPt = elements[0];
3653 for (
int i = 0; i < numElems; i++) {
3654 QPainterPath::Element e = elements[i];
3656 case QPainterPath::LineToElement:
3657 runLen += QLineF(runPt, e).length();
3660 case QPainterPath::CurveToElement: {
3661 Q_ASSERT(i < numElems - 2);
3662 QPainterPath::Element ee = elements[i + 2];
3663 runLen += QBezier::fromPoints(runPt, e, elements[i + 1], ee).length();
3667 case QPainterPath::MoveToElement:
3670 case QPainterPath::CurveToDataElement:
3673 m_runLengths.append(runLen);
3675 Q_ASSERT(m_runLengths.size() == elements.size());
3677 dirtyRunLengths =
false;
3680#ifndef QT_NO_DEBUG_STREAM
3683 QDebugStateSaver saver(s);
3684 s.nospace() <<
"QPainterPath: Element count=" << p.elementCount() << Qt::endl;
3685 const char *types[] = {
"MoveTo",
"LineTo",
"CurveTo",
"CurveToData"};
3686 for (
int i=0; i<p.elementCount(); ++i) {
3687 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()
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)