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 (QtPrivate::fuzzyCompare(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, last_start)
1999 && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
2000 last_start.x(), last_start.y(), rect))
2002 last_start = last_pt = e;
2005 case QPainterPath::LineToElement:
2006 if (qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(), e.x, e.y, rect))
2011 case QPainterPath::CurveToElement:
2013 QPointF cp2 = path->elementAt(++i);
2014 QPointF ep = path->elementAt(++i);
2015 QBezier bezier = QBezier::fromPoints(last_pt, e, cp2, ep);
2016 if (qt_isect_curve_horizontal(bezier, rect.top(), rect.left(), rect.right())
2017 || qt_isect_curve_horizontal(bezier, rect.bottom(), rect.left(), rect.right())
2018 || qt_isect_curve_vertical(bezier, rect.left(), rect.top(), rect.bottom())
2019 || qt_isect_curve_vertical(bezier, rect.right(), rect.top(), rect.bottom()))
2030 if (!pointOnEdge(rect, last_pt)) {
2031 bool contained = rect.contains(last_pt);
2032 switch (edgeStatus) {
2042 edgeStatus = contained ? InsideRect : OutsideRect;
2046 if (last_pt == last_start)
2047 edgeStatus = OnRect;
2052 if (last_pt != last_start
2053 && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
2054 last_start.x(), last_start.y(), rect))
2061
2062
2063
2064
2065
2066
2067
2068
2069
2070
2071
2072
2073
2074bool QPainterPath::intersects(
const QRectF &rect)
const
2076 if (elementCount() == 1 && rect.contains(elementAt(0)))
2082 QRectF cp = controlPointRect();
2083 QRectF rn = rect.normalized();
2088 if (qMax(rn.left(), cp.left()) > qMin(rn.right(), cp.right())
2089 || qMax(rn.top(), cp.top()) > qMin(rn.bottom(), cp.bottom()))
2093 if (qt_painterpath_check_crossing(
this, rect))
2096 if (contains(rect.center()))
2102 for (
int i=0; i<d->elements.size(); ++i) {
2103 const Element &e = d->elements.at(i);
2104 if (e.type == QPainterPath::MoveToElement && rect.contains(e))
2112
2113
2114
2115
2116
2117void QPainterPath::translate(qreal dx, qreal dy)
2119 if (!d_ptr || (dx == 0 && dy == 0))
2122 int elementsLeft = d_ptr->elements.size();
2123 if (elementsLeft <= 0)
2127 QPainterPath::Element *element = d_func()->elements.data();
2129 while (elementsLeft--) {
2137
2138
2139
2140
2141
2142
2143
2144
2147
2148
2149
2150
2151
2152QPainterPath QPainterPath::translated(qreal dx, qreal dy)
const
2154 QPainterPath copy(*
this);
2155 copy.translate(dx, dy);
2160
2161
2162
2163
2164
2165
2166
2167
2170
2171
2172
2173
2174
2175bool QPainterPath::contains(
const QRectF &rect)
const
2181 if (isEmpty() || !controlPointRect().contains(rect))
2187 if (qt_painterpath_check_crossing(
this, rect)) {
2188 if (fillRule() == Qt::OddEvenFill) {
2193 if (!contains(rect.topLeft()) ||
2194 !contains(rect.topRight()) ||
2195 !contains(rect.bottomRight()) ||
2196 !contains(rect.bottomLeft()))
2205 if (!contains(rect.center()))
2214 for (
int i=0; i<d->elements.size(); ++i) {
2215 const Element &e = d->elements.at(i);
2216 if (e.type == QPainterPath::MoveToElement && rect.contains(e)) {
2217 if (fillRule() == Qt::OddEvenFill)
2221 for (; !stop && i<d->elements.size(); ++i) {
2222 const Element &el = d->elements.at(i);
2231 case CurveToElement:
2232 if (!contains(d->elements.at(i+2)))
2249static inline bool epsilonCompare(
const QPointF &a,
const QPointF &b,
const QSizeF &epsilon)
2251 return qAbs(a.x() - b.x()) <= epsilon.width()
2252 && qAbs(a.y() - b.y()) <= epsilon.height();
2256
2257
2258
2259
2260
2261
2262
2264bool QPainterPath::operator==(
const QPainterPath &path)
const
2266 QPainterPathPrivate *d = d_func();
2267 QPainterPathPrivate *other_d = path.d_func();
2270 }
else if (!d || !other_d) {
2271 if (!other_d && isEmpty() && elementAt(0) == QPointF() && !d->hasWindingFill)
2273 if (!d && path.isEmpty() && path.elementAt(0) == QPointF() && !other_d->hasWindingFill)
2277 else if (d->hasWindingFill != other_d->hasWindingFill)
2279 else if (d->elements.size() != other_d->elements.size())
2282 const qreal qt_epsilon =
sizeof(qreal) ==
sizeof(
double) ? 1e-12 : qreal(1e-5);
2284 QSizeF epsilon = boundingRect().size();
2285 epsilon.rwidth() *= qt_epsilon;
2286 epsilon.rheight() *= qt_epsilon;
2288 for (
int i = 0; i < d->elements.size(); ++i)
2289 if (d->elements.at(i).type != other_d->elements.at(i).type
2290 || !epsilonCompare(d->elements.at(i), other_d->elements.at(i), epsilon))
2297
2298
2299
2300
2301
2302
2303
2305bool QPainterPath::operator!=(
const QPainterPath &path)
const
2307 return !(*
this==path);
2311
2312
2313
2314
2315
2316
2317QPainterPath QPainterPath::operator&(
const QPainterPath &other)
const
2319 return intersected(other);
2323
2324
2325
2326
2327
2328
2329QPainterPath QPainterPath::operator|(
const QPainterPath &other)
const
2331 return united(other);
2335
2336
2337
2338
2339
2340
2341
2342QPainterPath QPainterPath::operator+(
const QPainterPath &other)
const
2344 return united(other);
2348
2349
2350
2351
2352
2353
2354QPainterPath QPainterPath::operator-(
const QPainterPath &other)
const
2356 return subtracted(other);
2360
2361
2362
2363
2364
2365
2366QPainterPath &QPainterPath::operator&=(
const QPainterPath &other)
2368 return *
this = (*
this & other);
2372
2373
2374
2375
2376
2377
2378QPainterPath &QPainterPath::operator|=(
const QPainterPath &other)
2380 return *
this = (*
this | other);
2384
2385
2386
2387
2388
2389
2390
2391QPainterPath &QPainterPath::operator+=(
const QPainterPath &other)
2393 return *
this = (*
this + other);
2397
2398
2399
2400
2401
2402
2403
2404QPainterPath &QPainterPath::operator-=(
const QPainterPath &other)
2406 return *
this = (*
this - other);
2409#ifndef QT_NO_DATASTREAM
2411
2412
2413
2414
2415
2416
2417
2418
2426 s << p.elementCount();
2427 for (
int i=0; i < p.d_func()->elements.size(); ++i) {
2428 const QPainterPath::Element &e = p.d_func()->elements.at(i);
2430 s <<
double(e.x) <<
double(e.y);
2432 s << p.d_func()->cStart;
2433 s <<
int(p.fillRule());
2438
2439
2440
2441
2442
2443
2444
2445
2448 bool errorDetected =
false;
2459 p.d_func()->elements.clear();
2460 for (
int i=0; i<size; ++i) {
2466 Q_ASSERT(type >= 0 && type <= 3);
2467 if (!isValidCoord(qreal(x)) || !isValidCoord(qreal(y))) {
2469 qWarning(
"QDataStream::operator>>: Invalid QPainterPath coordinates read, skipping it");
2471 errorDetected =
true;
2474 QPainterPath::Element elm = { qreal(x), qreal(y), QPainterPath::ElementType(type) };
2475 p.d_func()->elements.append(elm);
2477 s >> p.d_func()->cStart;
2480 Q_ASSERT(fillRule == Qt::OddEvenFill || fillRule == Qt::WindingFill);
2481 p.d_func()->hasWindingFill = (Qt::FillRule(fillRule) == Qt::WindingFill);
2482 if (errorDetected || p.d_func()->elements.isEmpty())
2490
2491
2495 ((QPainterPath *) data)->moveTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
2500 ((QPainterPath *) data)->lineTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
2504 qfixed c2x, qfixed c2y,
2505 qfixed ex, qfixed ey,
2508 ((QPainterPath *) data)->cubicTo(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y),
2509 qt_fixed_to_real(c2x), qt_fixed_to_real(c2y),
2510 qt_fixed_to_real(ex), qt_fixed_to_real(ey));
2514
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
2567 stroker.setMoveToHook(qt_path_stroke_move_to);
2568 stroker.setLineToHook(qt_path_stroke_line_to);
2569 stroker.setCubicToHook(qt_path_stroke_cubic_to);
2573
2574
2575QPainterPathStroker::QPainterPathStroker()
2576 : d_ptr(
new QPainterPathStrokerPrivate)
2581
2582
2583
2584
2585QPainterPathStroker::QPainterPathStroker(
const QPen &pen)
2586 : d_ptr(
new QPainterPathStrokerPrivate)
2588 setWidth(pen.widthF());
2589 setCapStyle(pen.capStyle());
2590 setJoinStyle(pen.joinStyle());
2591 setMiterLimit(pen.miterLimit());
2592 setDashOffset(pen.dashOffset());
2594 if (pen.style() == Qt::CustomDashLine)
2595 setDashPattern(pen.dashPattern());
2597 setDashPattern(pen.style());
2601
2602
2603QPainterPathStroker::~QPainterPathStroker()
2609
2610
2611
2612
2613
2614
2615
2616
2617
2618
2619
2620
2621QPainterPath QPainterPathStroker::createStroke(
const QPainterPath &path)
const
2623 QPainterPathStrokerPrivate *d =
const_cast<QPainterPathStrokerPrivate *>(d_func());
2624 QPainterPath stroke;
2627 if (d->dashPattern.isEmpty()) {
2628 d->stroker.strokePath(path, &stroke, QTransform());
2630 QDashStroker dashStroker(&d->stroker);
2631 dashStroker.setDashPattern(d->dashPattern);
2632 dashStroker.setDashOffset(d->dashOffset);
2633 dashStroker.setClipRect(d->stroker.clipRect());
2634 dashStroker.strokePath(path, &stroke, QTransform());
2636 stroke.setFillRule(Qt::WindingFill);
2641
2642
2643
2644
2645
2646void QPainterPathStroker::setWidth(qreal width)
2648 Q_D(QPainterPathStroker);
2651 d->stroker.setStrokeWidth(qt_real_to_fixed(width));
2655
2656
2657qreal QPainterPathStroker::width()
const
2659 return qt_fixed_to_real(d_func()->stroker.strokeWidth());
2664
2665
2666
2667
2668void QPainterPathStroker::setCapStyle(Qt::PenCapStyle style)
2670 d_func()->stroker.setCapStyle(style);
2675
2676
2677Qt::PenCapStyle QPainterPathStroker::capStyle()
const
2679 return d_func()->stroker.capStyle();
2683
2684
2685void QPainterPathStroker::setJoinStyle(Qt::PenJoinStyle style)
2687 d_func()->stroker.setJoinStyle(style);
2691
2692
2693Qt::PenJoinStyle QPainterPathStroker::joinStyle()
const
2695 return d_func()->stroker.joinStyle();
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708void QPainterPathStroker::setMiterLimit(qreal limit)
2710 d_func()->stroker.setMiterLimit(qt_real_to_fixed(limit));
2714
2715
2716qreal QPainterPathStroker::miterLimit()
const
2718 return qt_fixed_to_real(d_func()->stroker.miterLimit());
2723
2724
2725
2726
2727
2728
2729
2730void QPainterPathStroker::setCurveThreshold(qreal threshold)
2732 d_func()->stroker.setCurveThreshold(qt_real_to_fixed(threshold));
2736
2737
2738
2739qreal QPainterPathStroker::curveThreshold()
const
2741 return qt_fixed_to_real(d_func()->stroker.curveThreshold());
2745
2746
2747void QPainterPathStroker::setDashPattern(Qt::PenStyle style)
2749 d_func()->dashPattern = QDashStroker::patternForStyle(style);
2753
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768void QPainterPathStroker::setDashPattern(
const QList<qreal> &dashPattern)
2770 d_func()->dashPattern.clear();
2771 for (
int i=0; i<dashPattern.size(); ++i)
2772 d_func()->dashPattern << qt_real_to_fixed(dashPattern.at(i));
2776
2777
2778QList<qreal> QPainterPathStroker::dashPattern()
const
2780 return d_func()->dashPattern;
2784
2785
2786qreal QPainterPathStroker::dashOffset()
const
2788 return d_func()->dashOffset;
2792
2793
2794
2795
2796
2797void QPainterPathStroker::setDashOffset(qreal offset)
2799 d_func()->dashOffset = offset;
2803
2804
2805
2806
2807
2808
2809
2810
2811
2812
2813
2814
2815
2816
2817QPolygonF QPainterPath::toFillPolygon(
const QTransform &matrix)
const
2819 const QList<QPolygonF> flats = toSubpathPolygons(matrix);
2821 if (flats.isEmpty())
2823 QPointF first = flats.first().first();
2824 for (
int i=0; i<flats.size(); ++i) {
2825 polygon += flats.at(i);
2826 if (!flats.at(i).isClosed())
2827 polygon += flats.at(i).first();
2835
2836
2837
2838
2839
2840bool QPainterPath::isCachingEnabled()
const
2843 return d && d->cacheEnabled;
2847
2848
2849
2850
2851
2852
2853
2854
2855
2856
2857
2858void QPainterPath::setCachingEnabled(
bool enabled)
2861 if (d_func()->cacheEnabled == enabled)
2864 QPainterPathPrivate *d = d_func();
2865 d->cacheEnabled = enabled;
2867 d->m_runLengths.clear();
2868 d->m_runLengths.squeeze();
2875 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
2879
2880
2881qreal QPainterPath::length()
const
2886 if (d->cacheEnabled) {
2887 if (d->dirtyRunLengths)
2888 d->computeRunLengths();
2889 return d->m_runLengths.last();
2893 for (
int i=1; i<d->elements.size(); ++i) {
2894 const Element &e = d->elements.at(i);
2901 len += QLineF(d->elements.at(i-1), e).length();
2904 case CurveToElement:
2906 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
2908 d->elements.at(i+1),
2909 d->elements.at(i+2));
2922
2923
2924
2925
2926
2927
2928
2929qreal QPainterPath::percentAtLength(qreal len)
const
2932 if (isEmpty() || len <= 0)
2935 qreal totalLength = length();
2936 if (len > totalLength)
2939 Q_ASSERT(totalLength != 0);
2941 if (d->cacheEnabled) {
2942 const int ei = qMax(d->elementAtT(len / totalLength), 1);
2944 const QPainterPath::Element &e = d->elements[ei];
2946 case QPainterPath::LineToElement:
2947 res = len / totalLength;
2949 case CurveToElement:
2951 QBezier b = QBezier::fromPoints(d->elements.at(ei-1),
2953 d->elements.at(ei+1),
2954 d->elements.at(ei+2));
2955 qreal prevLen = d->m_runLengths[ei - 1];
2956 qreal blen = d->m_runLengths[ei] - prevLen;
2957 qreal elemRes = b.tAtLength(len - prevLen);
2958 res = (elemRes * blen + prevLen) / totalLength;
2968 for (
int i=1; i<d->elements.size(); ++i) {
2969 const Element &e = d->elements.at(i);
2976 QLineF line(d->elements.at(i-1), e);
2977 qreal llen = line.length();
2979 if (curLen >= len) {
2980 return len/totalLength ;
2985 case CurveToElement:
2987 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
2989 d->elements.at(i+1),
2990 d->elements.at(i+2));
2991 qreal blen = b.length();
2992 qreal prevLen = curLen;
2995 if (curLen >= len) {
2996 qreal res = b.tAtLength(len - prevLen);
2997 return (res * blen + prevLen)/totalLength;
3012 qreal *bezierLength)
3014 *startingLength = 0;
3019 qreal totalLength = path.length();
3021 const int lastElement = path.elementCount() - 1;
3022 for (
int i=0; i <= lastElement; ++i) {
3023 const QPainterPath::Element &e = path.elementAt(i);
3026 case QPainterPath::MoveToElement:
3028 case QPainterPath::LineToElement:
3030 QLineF line(path.elementAt(i-1), e);
3031 qreal llen = line.length();
3033 if (i == lastElement || curLen/totalLength >= t) {
3034 *bezierLength = llen;
3035 QPointF a = path.elementAt(i-1);
3036 QPointF delta = e - a;
3037 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
3041 case QPainterPath::CurveToElement:
3043 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
3045 path.elementAt(i+1),
3046 path.elementAt(i+2));
3047 qreal blen = b.length();
3050 if (i + 2 == lastElement || curLen/totalLength >= t) {
3051 *bezierLength = blen;
3061 *startingLength = curLen;
3067 qreal *bezierLength)
const
3069 Q_ASSERT(t >= 0 && t <= 1);
3071 if (!path.isEmpty() && d->cacheEnabled) {
3072 const int ei = qMax(d->elementAtT(t), 1);
3073 const qreal prevRunLength = d->m_runLengths[ei - 1];
3074 *startingLength = prevRunLength;
3075 *bezierLength = d->m_runLengths[ei] - prevRunLength;
3076 const QPointF prev = d->elements[ei - 1];
3077 const QPainterPath::Element &e = d->elements[ei];
3079 case QPainterPath::LineToElement:
3081 QPointF delta = (e - prev) / 3;
3082 return QBezier::fromPoints(prev, prev + delta, prev + 2 * delta, e);
3084 case QPainterPath::CurveToElement:
3085 return QBezier::fromPoints(prev, e, elements[ei + 1], elements[ei + 2]);
3092 return uncached_bezierAtT(path, t, startingLength, bezierLength);
3096
3097
3098
3099
3100
3101
3102
3103
3104QPointF QPainterPath::pointAtPercent(qreal t)
const
3106 if (t < 0 || t > 1) {
3107 qWarning(
"QPainterPath::pointAtPercent accepts only values between 0 and 1");
3111 if (!d_ptr || d_ptr->elements.size() == 0)
3114 if (d_ptr->elements.size() == 1)
3115 return d_ptr->elements.at(0);
3117 qreal totalLength = length();
3119 qreal bezierLen = 0;
3120 QBezier b = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3121 Q_ASSERT(bezierLen != 0);
3122 qreal realT = (totalLength * t - curLen) / bezierLen;
3124 return b.pointAt(qBound(qreal(0), realT, qreal(1)));
3128
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139qreal QPainterPath::angleAtPercent(qreal t)
const
3141 if (t < 0 || t > 1) {
3142 qWarning(
"QPainterPath::angleAtPercent accepts only values between 0 and 1");
3149 qreal totalLength = length();
3151 qreal bezierLen = 0;
3152 QBezier bez = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3153 Q_ASSERT(bezierLen != 0);
3154 qreal realT = (totalLength * t - curLen) / bezierLen;
3156 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3157 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3159 return QLineF(0, 0, m1, m2).angle();
3164
3165
3166
3167
3168
3169
3170
3171
3172qreal QPainterPath::slopeAtPercent(qreal t)
const
3174 if (t < 0 || t > 1) {
3175 qWarning(
"QPainterPath::slopeAtPercent accepts only values between 0 and 1");
3182 qreal totalLength = length();
3184 qreal bezierLen = 0;
3185 QBezier bez = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3186 Q_ASSERT(bezierLen != 0);
3187 qreal realT = (totalLength * t - curLen) / bezierLen;
3189 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3190 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3197 if (std::numeric_limits<qreal>::has_infinity) {
3198 slope = (m2 < 0) ? -std::numeric_limits<qreal>::infinity()
3199 : std::numeric_limits<qreal>::infinity();
3201 if (
sizeof(qreal) ==
sizeof(
double)) {
3202 return 1.79769313486231570e+308;
3204 return ((qreal)3.40282346638528860e+38);
3213
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3229QPainterPath QPainterPath::trimmed(qreal fromFraction, qreal toFraction, qreal offset)
const
3235 if (!isCachingEnabled()) {
3236 QPainterPath copy(*
this);
3237 copy.setCachingEnabled(
true);
3238 return copy.trimmed(fromFraction, toFraction, offset);
3241 qreal f1 = qBound(qreal(0), fromFraction, qreal(1));
3242 qreal f2 = qBound(qreal(0), toFraction, qreal(1));
3243 if (qFuzzyIsNull(f1 - f2))
3244 return QPainterPath();
3247 if (qFuzzyCompare(f2 - f1, qreal(1)))
3251 res.setFillRule(fillRule());
3255 offset = std::modf(offset, &dummy);
3257 qreal of1 = f1 + offset;
3258 qreal of2 = f2 + offset;
3260 f1 = of1 < 0 ? of1 + 1 : of1;
3261 f2 = of2 + 1 > 1 ? of2 : of2 + 1;
3262 }
else if (offset > 0) {
3263 f1 = of1 - 1 < 0 ? of1 : of1 - 1;
3264 f2 = of2 > 1 ? of2 - 1 : of2;
3267 const bool wrapping = (f1 > f2);
3270 QPainterPathPrivate *d = d_func();
3271 if (d->dirtyRunLengths)
3272 d->computeRunLengths();
3273 const qreal totalLength = d->m_runLengths.last();
3274 if (qFuzzyIsNull(totalLength))
3277 const qreal l1 = f1 * totalLength;
3278 const qreal l2 = f2 * totalLength;
3279 const int e1 = d->elementAtLength(l1);
3280 const bool mustTrimE1 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e1), l1);
3281 const int e2 = d->elementAtLength(l2);
3282 const bool mustTrimE2 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e2), l2);
3285 if (e1 == e2 && !wrapping && mustTrimE1 && mustTrimE2) {
3287 d->appendSliceOfElement(&res, e1, l1, l2);
3291 d->appendEndOfElement(&res, e1, l1);
3293 res.moveTo(d->endPointOfElement(e1));
3296 int firstWholeElement = e1 + 1;
3297 int lastWholeElement = (mustTrimE2 ? e2 - 1 : e2);
3299 d->appendElementRange(&res, firstWholeElement, lastWholeElement);
3301 int lastIndex = d->elements.size() - 1;
3302 d->appendElementRange(&res, firstWholeElement, lastIndex);
3303 bool isClosed = (QPointF(d->elements.at(0)) == QPointF(d->elements.at(lastIndex)));
3305 d->appendElementRange(&res, (isClosed ? 1 : 0), lastWholeElement);
3310 d->appendStartOfElement(&res, e2, l2);
3317 qreal startLen, qreal endLen)
3319 Q_ASSERT(cacheEnabled);
3320 Q_ASSERT(!dirtyRunLengths);
3322 if (elemIdx <= 0 || elemIdx >= elements.size())
3325 const qreal prevLen = m_runLengths.at(elemIdx - 1);
3326 const qreal elemLen = m_runLengths.at(elemIdx) - prevLen;
3327 const qreal len1 = startLen - prevLen;
3328 const qreal len2 = endLen - prevLen;
3329 if (qFuzzyIsNull(elemLen))
3332 const QPointF pp = elements.at(elemIdx - 1);
3333 const QPainterPath::Element e = elements.at(elemIdx);
3336 QPointF p1 = (trimFlags & TrimStart) ? l.pointAt(len1 / elemLen) : pp;
3337 QPointF p2 = (trimFlags & TrimEnd) ? l.pointAt(len2 / elemLen) : e;
3341 }
else if (e.isCurveTo()) {
3342 Q_ASSERT(elemIdx < elements.size() - 2);
3343 QBezier b = QBezier::fromPoints(pp, e, elements.at(elemIdx + 1), elements.at(elemIdx + 2));
3344 qreal t1 = (trimFlags & TrimStart) ? b.tAtLength(len1) : 0.0;
3345 qreal t2 = (trimFlags & TrimEnd) ? b.tAtLength(len2) : 1.0;
3346 QBezier c = b.getSubRange(t1, t2);
3348 to->moveTo(c.pt1());
3349 to->cubicTo(c.pt2(), c.pt3(), c.pt4());
3357 if (first < 0 || first >= elements.size() || last < 0 || last >= elements.size())
3361 for (
int i = first; i <= last; i++) {
3362 const QPainterPath::Element &e = elements.at(i);
3364 case QPainterPath::MoveToElement:
3367 case QPainterPath::LineToElement:
3370 case QPainterPath::CurveToElement:
3371 Q_ASSERT(i < elements.size() - 2);
3372 to->cubicTo(e, elements.at(i + 1), elements.at(i + 2));
3384
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396void QPainterPath::addRoundedRect(
const QRectF &rect, qreal xRadius, qreal yRadius,
3399 QRectF r = rect.normalized();
3404 if (mode == Qt::AbsoluteSize) {
3405 qreal w = r.width() / 2;
3406 qreal h = r.height() / 2;
3411 xRadius = 100 * qMin(xRadius, w) / w;
3416 yRadius = 100 * qMin(yRadius, h) / h;
3426 if (xRadius <= 0 || yRadius <= 0) {
3433 qreal w = r.width();
3434 qreal h = r.height();
3435 qreal rxx2 = w*xRadius/100;
3436 qreal ryy2 = h*yRadius/100;
3441 bool first = d_func()->elements.size() < 2;
3443 arcMoveTo(x, y, rxx2, ryy2, 180);
3444 arcTo(x, y, rxx2, ryy2, 180, -90);
3445 arcTo(x+w-rxx2, y, rxx2, ryy2, 90, -90);
3446 arcTo(x+w-rxx2, y+h-ryy2, rxx2, ryy2, 0, -90);
3447 arcTo(x, y+h-ryy2, rxx2, ryy2, 270, -90);
3450 d_func()->require_moveTo =
true;
3451 d_func()->convex = first;
3455
3456
3457
3458
3459
3460
3463
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474QPainterPath QPainterPath::united(
const QPainterPath &p)
const
3476 if (isEmpty() || p.isEmpty())
3477 return isEmpty() ? p : *
this;
3478 QPathClipper clipper(*
this, p);
3479 return clipper.clip(QPathClipper::BoolOr);
3483
3484
3485
3486
3487
3488
3489QPainterPath QPainterPath::intersected(
const QPainterPath &p)
const
3491 if (isEmpty() || p.isEmpty())
3492 return QPainterPath();
3493 QPathClipper clipper(*
this, p);
3494 return clipper.clip(QPathClipper::BoolAnd);
3498
3499
3500
3501
3502
3503
3504
3505
3506
3507QPainterPath QPainterPath::subtracted(
const QPainterPath &p)
const
3509 if (isEmpty() || p.isEmpty())
3511 QPathClipper clipper(*
this, p);
3512 return clipper.clip(QPathClipper::BoolSub);
3516
3517
3518
3519
3520
3521
3522
3523
3524QPainterPath QPainterPath::simplified()
const
3528 QPathClipper clipper(*
this, QPainterPath());
3529 return clipper.clip(QPathClipper::Simplify);
3533
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543bool QPainterPath::intersects(
const QPainterPath &p)
const
3545 if (p.elementCount() == 1)
3546 return contains(p.elementAt(0));
3547 if (isEmpty() || p.isEmpty())
3549 QPathClipper clipper(*
this, p);
3550 return clipper.intersect();
3554
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565bool QPainterPath::contains(
const QPainterPath &p)
const
3567 if (p.elementCount() == 1)
3568 return contains(p.elementAt(0));
3569 if (isEmpty() || p.isEmpty())
3571 QPathClipper clipper(*
this, p);
3572 return clipper.contains();
3575void QPainterPath::setDirty(
bool dirty)
3577 d_func()->pathConverter.reset();
3578 d_func()->dirtyBounds = dirty;
3579 d_func()->dirtyControlBounds = dirty;
3580 d_func()->dirtyRunLengths = dirty;
3581 d_func()->convex =
false;
3584void QPainterPath::computeBoundingRect()
const
3586 QPainterPathPrivate *d = d_func();
3587 d->dirtyBounds =
false;
3589 d->bounds = QRect();
3593 qreal minx, maxx, miny, maxy;
3594 minx = maxx = d->elements.at(0).x;
3595 miny = maxy = d->elements.at(0).y;
3596 for (
int i=1; i<d->elements.size(); ++i) {
3597 const Element &e = d->elements.at(i);
3602 if (e.x > maxx) maxx = e.x;
3603 else if (e.x < minx) minx = e.x;
3604 if (e.y > maxy) maxy = e.y;
3605 else if (e.y < miny) miny = e.y;
3607 case CurveToElement:
3609 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
3611 d->elements.at(i+1),
3612 d->elements.at(i+2));
3613 QRectF r = qt_painterpath_bezier_extrema(b);
3614 qreal right = r.right();
3615 qreal bottom = r.bottom();
3616 if (r.x() < minx) minx = r.x();
3617 if (right > maxx) maxx = right;
3618 if (r.y() < miny) miny = r.y();
3619 if (bottom > maxy) maxy = bottom;
3627 d->bounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3631void QPainterPath::computeControlPointRect()
const
3633 QPainterPathPrivate *d = d_func();
3634 d->dirtyControlBounds =
false;
3636 d->controlBounds = QRect();
3640 qreal minx, maxx, miny, maxy;
3641 minx = maxx = d->elements.at(0).x;
3642 miny = maxy = d->elements.at(0).y;
3643 for (
int i=1; i<d->elements.size(); ++i) {
3644 const Element &e = d->elements.at(i);
3645 if (e.x > maxx) maxx = e.x;
3646 else if (e.x < minx) minx = e.x;
3647 if (e.y > maxy) maxy = e.y;
3648 else if (e.y < miny) miny = e.y;
3650 d->controlBounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3655 Q_ASSERT(!elements.isEmpty());
3657 m_runLengths.clear();
3658 const int numElems = elements.size();
3659 m_runLengths.reserve(numElems);
3661 QPointF runPt = elements[0];
3663 for (
int i = 0; i < numElems; i++) {
3664 QPainterPath::Element e = elements[i];
3666 case QPainterPath::LineToElement:
3667 runLen += QLineF(runPt, e).length();
3670 case QPainterPath::CurveToElement: {
3671 Q_ASSERT(i < numElems - 2);
3672 QPainterPath::Element ee = elements[i + 2];
3673 runLen += QBezier::fromPoints(runPt, e, elements[i + 1], ee).length();
3677 case QPainterPath::MoveToElement:
3680 case QPainterPath::CurveToDataElement:
3683 m_runLengths.append(runLen);
3685 Q_ASSERT(m_runLengths.size() == elements.size());
3687 dirtyRunLengths =
false;
3690#ifndef QT_NO_DEBUG_STREAM
3693 QDebugStateSaver saver(s);
3694 s.nospace() <<
"QPainterPath: Element count=" << p.elementCount() << Qt::endl;
3695 const char *types[] = {
"MoveTo",
"LineTo",
"CurveTo",
"CurveToData"};
3696 for (
int i=0; i<p.elementCount(); ++i) {
3697 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)