14#include <qtextlayout.h>
15#include <qvarlengtharray.h>
18#include <private/qbezier_p.h>
19#include <private/qfontengine_p.h>
20#include <private/qnumeric_p.h>
21#include <private/qobject_p.h>
22#include <private/qpathclipper_p.h>
23#include <private/qstroker_p.h>
24#include <private/qtextengine_p.h>
31#include <performance.h>
42 if (
sizeof(qreal) >=
sizeof(
double))
43 return qIsFinite(c) && fabs(c) < 1e128;
45 return qIsFinite(c) && fabsf(
float(c)) < 1e16f;
50 return isValidCoord(p.x()) && isValidCoord(p.y());
55 return isValidCoord(r.x()) && isValidCoord(r.y()) && isValidCoord(r.width()) && isValidCoord(r.height());
69 QPointF* startPoint, QPointF *endPoint)
73 *startPoint = QPointF();
75 *endPoint = QPointF();
79 qreal w2 = r.width() / 2;
80 qreal h2 = r.height() / 2;
82 qreal angles[2] = { angle, angle + length };
83 QPointF *points[2] = { startPoint, endPoint };
85 for (
int i = 0; i < 2; ++i) {
89 qreal theta = angles[i] - 360 * qFloor(angles[i] / 360);
92 int quadrant =
int(t);
95 t = qt_t_for_arc_angle(90 * t);
102 QBezier::coefficients(t, a, b, c, d);
103 QPointF p(a + b + c*QT_PATH_KAPPA, d + c + b*QT_PATH_KAPPA);
106 if (quadrant == 1 || quadrant == 2)
110 if (quadrant == 0 || quadrant == 1)
113 *points[i] = r.center() + QPointF(w2 * p.x(), h2 * p.y());
118static void qt_debug_path(
const QPainterPath &path)
120 const char *names[] = {
127 printf(
"\nQPainterPath: elementCount=%d\n", path.elementCount());
128 for (
int i=0; i<path.elementCount(); ++i) {
129 const QPainterPath::Element &e = path.elementAt(i);
130 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
131 printf(
" - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
344
345
346
347
348
351
352
353
354
355
358
359
360
361
362
365
366
367
368
369
370
371
372
375
376
377
378
379
380
381
382
385
386
387
388
389
390
393
394
395
396
397
398
401
402
403
404
405
406
407
410
411
412
413
414
415
418
419
420
421
422
423
424
427
428
429
430
431
432
433
434
437
438
439
440
441
442
443
446
447
448
449
450
451
453int QPainterPath::elementCount()
const
455 return d_ptr ? d_ptr->elements.size() : 0;
459
460
461
462
463
464
466QPainterPath::Element QPainterPath::elementAt(
int i)
const
469 Q_ASSERT(i >= 0 && i < elementCount());
470 return d_ptr->elements.at(i);
474
475
476
477
478
479
481void QPainterPath::setElementPositionAt(
int i, qreal x, qreal y)
484 Q_ASSERT(i >= 0 && i < elementCount());
486 QPainterPath::Element &e = d_ptr->elements[i];
493
494
495
496
497
500
501
502QPainterPath::QPainterPath()
noexcept
508
509
510
511
512
513
514QPainterPath::QPainterPath(
const QPainterPath &other)
515 : d_ptr(other.d_ptr ?
new QPainterPathPrivate(*other.d_ptr) :
nullptr)
520
521
522
523
524
525
526
529
530
531
533QPainterPath::QPainterPath(
const QPointF &startPoint)
534 : d_ptr(
new QPainterPathPrivate(startPoint))
539
540
541void QPainterPath::ensureData_helper()
543 Q_ASSERT(d_ptr ==
nullptr);
544 QPainterPathPrivate *data =
new QPainterPathPrivate;
545 data->elements.reserve(16);
546 QPainterPath::Element e = { 0, 0, QPainterPath::MoveToElement };
549 Q_ASSERT(d_ptr !=
nullptr);
553
554
555
556
557
558
559QPainterPath &QPainterPath::operator=(
const QPainterPath &other)
561 QPainterPath copy(other);
567
568
569
570
571
572
575
576
577
578
581
582
583QPainterPath::~QPainterPath()
589
590
591
592
593
594
595
596void QPainterPath::clear()
603 d_func()->elements.append( {0, 0, MoveToElement} );
607
608
609
610
611
612
613
614void QPainterPath::reserve(
int size)
617 if ((!d && size > 0) || (d && d->elements.capacity() < size)) {
620 d_func()->elements.reserve(size);
625
626
627
628
629
630int QPainterPath::capacity()
const
634 return d->elements.capacity();
640
641
642
643
644
645
646
647
648
649
650void QPainterPath::closeSubpath()
653 printf(
"QPainterPath::closeSubpath()\n");
663
664
665
666
667
668
669
672
673
674
675
676
677
678
679
680void QPainterPath::moveTo(
const QPointF &p)
683 printf(
"QPainterPath::moveTo() (%.2f,%.2f)\n", p.x(), p.y());
686 if (!hasValidCoords(p)) {
688 qWarning(
"QPainterPath::moveTo: Adding point with invalid coordinates, ignoring call");
696 QPainterPathPrivate *d = d_func();
697 Q_ASSERT(!d->elements.isEmpty());
699 d->require_moveTo =
false;
701 if (d->elements.constLast().type == MoveToElement) {
702 d->elements.last().x = p.x();
703 d->elements.last().y = p.y();
705 Element elm = { p.x(), p.y(), MoveToElement };
706 d->elements.append(elm);
708 d->cStart = d->elements.size() - 1;
712
713
714
715
716
717
718
721
722
723
724
725
726
727
728
729
730void QPainterPath::lineTo(
const QPointF &p)
733 printf(
"QPainterPath::lineTo() (%.2f,%.2f)\n", p.x(), p.y());
736 if (!hasValidCoords(p)) {
738 qWarning(
"QPainterPath::lineTo: Adding point with invalid coordinates, ignoring call");
746 QPainterPathPrivate *d = d_func();
747 Q_ASSERT(!d->elements.isEmpty());
749 if (p == QPointF(d->elements.constLast()))
751 Element elm = { p.x(), p.y(), LineToElement };
752 d->elements.append(elm);
754 d->convex = d->elements.size() == 3 || (d->elements.size() == 4 && d->isClosed());
758
759
760
761
762
763
764
765
766
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788void QPainterPath::cubicTo(
const QPointF &c1,
const QPointF &c2,
const QPointF &e)
791 printf(
"QPainterPath::cubicTo() (%.2f,%.2f), (%.2f,%.2f), (%.2f,%.2f)\n",
792 c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y());
795 if (!hasValidCoords(c1) || !hasValidCoords(c2) || !hasValidCoords(e)) {
797 qWarning(
"QPainterPath::cubicTo: Adding point with invalid coordinates, ignoring call");
805 QPainterPathPrivate *d = d_func();
806 Q_ASSERT(!d->elements.isEmpty());
811 if (d->elements.constLast() == c1 && c1 == c2 && c2 == e)
816 Element ce1 = { c1.x(), c1.y(), CurveToElement };
817 Element ce2 = { c2.x(), c2.y(), CurveToDataElement };
818 Element ee = { e.x(), e.y(), CurveToDataElement };
819 d->elements << ce1 << ce2 << ee;
823
824
825
826
827
828
829
830
833
834
835
836
837
838
839
840
841
842
843
844void QPainterPath::quadTo(
const QPointF &c,
const QPointF &e)
847 printf(
"QPainterPath::quadTo() (%.2f,%.2f), (%.2f,%.2f)\n",
848 c.x(), c.y(), e.x(), e.y());
851 if (!hasValidCoords(c) || !hasValidCoords(e)) {
853 qWarning(
"QPainterPath::quadTo: Adding point with invalid coordinates, ignoring call");
862 Q_ASSERT(!d->elements.isEmpty());
863 const QPainterPath::Element &elm = d->elements.at(elementCount()-1);
864 QPointF prev(elm.x, elm.y);
868 if (prev == c && c == e)
871 QPointF c1((prev.x() + 2*c.x()) / 3, (prev.y() + 2*c.y()) / 3);
872 QPointF c2((e.x() + 2*c.x()) / 3, (e.y() + 2*c.y()) / 3);
877
878
879
880
881
882
883
884
885
886
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
914
915void QPainterPath::arcTo(
const QRectF &rect, qreal startAngle, qreal sweepLength)
918 printf(
"QPainterPath::arcTo() (%.2f, %.2f, %.2f, %.2f, angle=%.2f, sweep=%.2f\n",
919 rect.x(), rect.y(), rect.width(), rect.height(), startAngle, sweepLength);
922 if (!hasValidCoords(rect) || !isValidCoord(startAngle) || !isValidCoord(sweepLength)) {
924 qWarning(
"QPainterPath::arcTo: Adding point with invalid coordinates, ignoring call");
937 QPointF curve_start = qt_curves_for_arc(rect, startAngle, sweepLength, pts, &point_count);
940 for (
int i=0; i<point_count; i+=3) {
941 cubicTo(pts[i].x(), pts[i].y(),
942 pts[i+1].x(), pts[i+1].y(),
943 pts[i+2].x(), pts[i+2].y());
950
951
952
953
954
955
956
960
961
962
963
964
965
966
967
968
969
970
972void QPainterPath::arcMoveTo(
const QRectF &rect, qreal angle)
978 qt_find_ellipse_coords(rect, angle, 0, &pt,
nullptr);
985
986
987
988
989QPointF QPainterPath::currentPosition()
const
991 return !d_ptr || d_func()->elements.isEmpty()
993 : QPointF(d_func()->elements.constLast().x, d_func()->elements.constLast().y);
998
999
1000
1001
1002
1003
1004
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025void QPainterPath::addRect(
const QRectF &r)
1027 if (!hasValidCoords(r)) {
1029 qWarning(
"QPainterPath::addRect: Adding point with invalid coordinates, ignoring call");
1040 bool first = d_func()->elements.size() < 2;
1042 moveTo(r.x(), r.y());
1044 Element l1 = { r.x() + r.width(), r.y(), LineToElement };
1045 Element l2 = { r.x() + r.width(), r.y() + r.height(), LineToElement };
1046 Element l3 = { r.x(), r.y() + r.height(), LineToElement };
1047 Element l4 = { r.x(), r.y(), LineToElement };
1049 d_func()->elements << l1 << l2 << l3 << l4;
1050 d_func()->require_moveTo =
true;
1051 d_func()->convex = first;
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071void QPainterPath::addPolygon(
const QPolygonF &polygon)
1073 if (polygon.isEmpty())
1079 moveTo(polygon.constFirst());
1080 for (
int i=1; i<polygon.size(); ++i) {
1081 Element elm = { polygon.at(i).x(), polygon.at(i).y(), LineToElement };
1082 d_func()->elements << elm;
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105void QPainterPath::addEllipse(
const QRectF &boundingRect)
1107 if (!hasValidCoords(boundingRect)) {
1109 qWarning(
"QPainterPath::addEllipse: Adding point with invalid coordinates, ignoring call");
1114 if (boundingRect.isNull())
1120 bool first = d_func()->elements.size() < 2;
1124 QPointF start = qt_curves_for_arc(boundingRect, 0, -360, pts, &point_count);
1127 cubicTo(pts[0], pts[1], pts[2]);
1128 cubicTo(pts[3], pts[4], pts[5]);
1129 cubicTo(pts[6], pts[7], pts[8]);
1130 cubicTo(pts[9], pts[10], pts[11]);
1131 d_func()->require_moveTo =
true;
1133 d_func()->convex = first;
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157void QPainterPath::addText(
const QPointF &point,
const QFont &f,
const QString &text)
1165 QTextLayout layout(text, f);
1166 layout.setCacheEnabled(
true);
1168 QTextOption opt = layout.textOption();
1169 opt.setUseDesignMetrics(
true);
1170 layout.setTextOption(opt);
1172 QTextEngine *eng = layout.engine();
1173 layout.beginLayout();
1174 QTextLine line = layout.createLine();
1177 const QScriptLine &sl = eng->lines[0];
1178 if (!sl.length || !eng->layoutData)
1181 int nItems = eng->layoutData->items.size();
1186 QVarLengthArray<
int> visualOrder(nItems);
1187 QVarLengthArray<uchar> levels(nItems);
1188 for (
int i = 0; i < nItems; ++i)
1189 levels[i] = eng->layoutData->items.at(i).analysis.bidiLevel;
1190 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
1192 for (
int i = 0; i < nItems; ++i) {
1193 int item = visualOrder[i];
1194 const QScriptItem &si = eng->layoutData->items.at(item);
1196 if (si.analysis.flags < QScriptAnalysis::TabOrObject) {
1197 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
1198 QFontEngine *fe = eng->fontEngine(si);
1200 fe->addOutlineToPath(x, y, glyphs,
this,
1201 si.analysis.bidiLevel % 2
1202 ? QTextItem::RenderFlags(QTextItem::RightToLeft)
1203 : QTextItem::RenderFlags{});
1205 const qreal lw = fe->lineThickness().toReal();
1206 if (f.d->underline) {
1207 qreal pos = fe->underlinePosition().toReal();
1208 addRect(x, y + pos, si.width.toReal(), lw);
1210 if (f.d->overline) {
1211 qreal pos = fe->ascent().toReal() + 1;
1212 addRect(x, y - pos, si.width.toReal(), lw);
1214 if (f.d->strikeOut) {
1215 qreal pos = fe->ascent().toReal() / 3;
1216 addRect(x, y - pos, si.width.toReal(), lw);
1219 x += si.width.toReal();
1224
1225
1226
1227
1228
1229
1230
1231void QPainterPath::addPath(
const QPainterPath &other)
1233 if (other.isEmpty())
1239 QPainterPathPrivate *d = d_func();
1241 if (d->elements.constLast().type == MoveToElement)
1242 d->elements.remove(d->elements.size()-1);
1245 int cStart = d->elements.size() + other.d_func()->cStart;
1246 d->elements += other.d_func()->elements;
1249 d->require_moveTo = other.d_func()->isClosed();
1254
1255
1256
1257
1258
1259
1260
1261
1262void QPainterPath::connectPath(
const QPainterPath &other)
1264 if (other.isEmpty())
1270 QPainterPathPrivate *d = d_func();
1272 if (d->elements.constLast().type == MoveToElement)
1273 d->elements.remove(d->elements.size()-1);
1276 int cStart = d->elements.size() + other.d_func()->cStart;
1277 int first = d->elements.size();
1278 d->elements += other.d_func()->elements;
1281 d->elements[first].type = LineToElement;
1284 if (first > 0 && QPointF(d->elements.at(first)) == QPointF(d->elements.at(first - 1))) {
1285 d->elements.remove(first--);
1289 if (cStart != first)
1294
1295
1296
1297
1298
1299
1300void QPainterPath::addRegion(
const QRegion ®ion)
1305 for (
const QRect &rect : region)
1311
1312
1313
1314
1315Qt::FillRule QPainterPath::fillRule()
const
1317 return d_func() && d_func()->hasWindingFill ? Qt::WindingFill : Qt::OddEvenFill;
1321
1322
1323
1324
1325
1326
1327
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337void QPainterPath::setFillRule(Qt::FillRule fillRule)
1340 const bool isWindingRequested = (fillRule == Qt::WindingFill);
1341 if (d_func()->hasWindingFill == isWindingRequested)
1345 d_func()->hasWindingFill = isWindingRequested;
1348#define QT_BEZIER_A(bezier, coord) 3
* (-bezier.coord##1
1353#define QT_BEZIER_B(bezier, coord) 6
* (bezier.coord##1
1357#define QT_BEZIER_C(bezier, coord) 3
* (- bezier.coord##1
1360#define QT_BEZIER_CHECK_T(bezier, t)
1361 if (t >= 0
&& t <= 1
) {
1362 QPointF p(b.pointAt(t));
1363 if (p.x() < minx) minx = p.x();
1364 else if (p.x() > maxx) maxx = p.x();
1365 if (p.y() < miny) miny = p.y();
1366 else if (p.y() > maxy) maxy = p.y();
1372 qreal minx, miny, maxx, maxy;
1396 if (qFuzzyIsNull(ax)) {
1399 if (!qFuzzyIsNull(bx)) {
1405 const qreal tx = bx * bx - 4 * ax * cx;
1408 qreal temp = qSqrt(tx);
1409 qreal rcp = 1 / (2 * ax);
1410 qreal t1 = (-bx + temp) * rcp;
1413 qreal t2 = (-bx - temp) * rcp;
1426 if (qFuzzyIsNull(ay)) {
1429 if (!qFuzzyIsNull(by)) {
1435 const qreal ty = by * by - 4 * ay * cy;
1438 qreal temp = qSqrt(ty);
1439 qreal rcp = 1 / (2 * ay);
1440 qreal t1 = (-by + temp) * rcp;
1443 qreal t2 = (-by - temp) * rcp;
1448 return QRectF(minx, miny, maxx - minx, maxy - miny);
1452
1453
1454
1455
1456
1457QRectF QPainterPath::boundingRect()
const
1461 QPainterPathPrivate *d = d_func();
1464 computeBoundingRect();
1469
1470
1471
1472
1473
1474
1475
1476
1477
1478QRectF QPainterPath::controlPointRect()
const
1482 QPainterPathPrivate *d = d_func();
1484 if (d->dirtyControlBounds)
1485 computeControlPointRect();
1486 return d->controlBounds;
1491
1492
1493
1494
1495
1496
1497
1499bool QPainterPath::isEmpty()
const
1501 return !d_ptr || (d_ptr->elements.size() == 1 && d_ptr->elements.constFirst().type == MoveToElement);
1505
1506
1507
1508
1509
1510
1511
1512QPainterPath QPainterPath::toReversed()
const
1514 Q_D(
const QPainterPath);
1522 rev.moveTo(d->elements.at(d->elements.size()-1).x, d->elements.at(d->elements.size()-1).y);
1524 for (
int i=d->elements.size()-1; i>=1; --i) {
1525 const QPainterPath::Element &elm = d->elements.at(i);
1526 const QPainterPath::Element &prev = d->elements.at(i-1);
1529 rev.lineTo(prev.x, prev.y);
1532 rev.moveTo(prev.x, prev.y);
1534 case CurveToDataElement:
1537 const QPainterPath::Element &cp1 = d->elements.at(i-2);
1538 const QPainterPath::Element &sp = d->elements.at(i-3);
1539 Q_ASSERT(prev.type == CurveToDataElement);
1540 Q_ASSERT(cp1.type == CurveToElement);
1541 rev.cubicTo(prev.x, prev.y, cp1.x, cp1.y, sp.x, sp.y);
1546 Q_ASSERT(!
"qt_reversed_path");
1555
1556
1557
1558
1559
1560
1561
1562
1563
1564
1565
1566QList<QPolygonF> QPainterPath::toSubpathPolygons(
const QTransform &matrix)
const
1569 Q_D(
const QPainterPath);
1570 QList<QPolygonF> flatCurves;
1575 for (
int i=0; i<elementCount(); ++i) {
1576 const QPainterPath::Element &e = d->elements.at(i);
1578 case QPainterPath::MoveToElement:
1579 if (current.size() > 1)
1580 flatCurves += current;
1582 current.reserve(16);
1583 current += QPointF(e.x, e.y) * matrix;
1585 case QPainterPath::LineToElement:
1586 current += QPointF(e.x, e.y) * matrix;
1588 case QPainterPath::CurveToElement: {
1589 Q_ASSERT(d->elements.at(i+1).type == QPainterPath::CurveToDataElement);
1590 Q_ASSERT(d->elements.at(i+2).type == QPainterPath::CurveToDataElement);
1591 QBezier bezier = QBezier::fromPoints(QPointF(d->elements.at(i-1).x, d->elements.at(i-1).y) * matrix,
1592 QPointF(e.x, e.y) * matrix,
1593 QPointF(d->elements.at(i+1).x, d->elements.at(i+1).y) * matrix,
1594 QPointF(d->elements.at(i+2).x, d->elements.at(i+2).y) * matrix);
1595 bezier.addToPolygon(¤t);
1599 case QPainterPath::CurveToDataElement:
1600 Q_ASSERT(!
"QPainterPath::toSubpathPolygons(), bad element type");
1605 if (current.size()>1)
1606 flatCurves += current;
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633QList<QPolygonF> QPainterPath::toFillPolygons(
const QTransform &matrix)
const
1636 QList<QPolygonF> polys;
1638 QList<QPolygonF> subpaths = toSubpathPolygons(matrix);
1639 int count = subpaths.size();
1644 QList<QRectF> bounds;
1645 bounds.reserve(count);
1646 for (
int i=0; i<count; ++i)
1647 bounds += subpaths.at(i).boundingRect();
1649#ifdef QPP_FILLPOLYGONS_DEBUG
1650 printf(
"QPainterPath::toFillPolygons, subpathCount=%d\n", count);
1651 for (
int i=0; i<bounds.size(); ++i)
1652 qDebug() <<
" bounds" << i << bounds.at(i);
1655 QList< QList<
int> > isects;
1656 isects.resize(count);
1659 for (
int j=0; j<count; ++j) {
1660 if (subpaths.at(j).size() <= 2)
1662 QRectF cbounds = bounds.at(j);
1663 for (
int i=0; i<count; ++i) {
1664 if (cbounds.intersects(bounds.at(i))) {
1670#ifdef QPP_FILLPOLYGONS_DEBUG
1671 printf(
"Intersections before flattening:\n");
1672 for (
int i = 0; i < count; ++i) {
1674 for (
int j = 0; j < isects[i].size(); ++j) {
1675 printf(
"%d ", isects[i][j]);
1682 for (
int i=0; i<count; ++i) {
1683 const QList<
int> ¤t_isects = isects.at(i);
1684 for (
int j=0; j<current_isects.size(); ++j) {
1685 int isect_j = current_isects.at(j);
1688 const QList<
int> &isects_j = isects.at(isect_j);
1689 for (
int k = 0, size = isects_j.size(); k < size; ++k) {
1690 int isect_k = isects_j.at(k);
1691 if (isect_k != i && !isects.at(i).contains(isect_k)) {
1692 isects[i] += isect_k;
1695 isects[isect_j].clear();
1699#ifdef QPP_FILLPOLYGONS_DEBUG
1700 printf(
"Intersections after flattening:\n");
1701 for (
int i = 0; i < count; ++i) {
1703 for (
int j = 0; j < isects[i].size(); ++j) {
1704 printf(
"%d ", isects[i][j]);
1711 for (
int i=0; i<count; ++i) {
1712 const QList<
int> &subpath_list = isects.at(i);
1713 if (!subpath_list.isEmpty()) {
1715 for (
int j=0; j<subpath_list.size(); ++j) {
1716 const QPolygonF &subpath = subpaths.at(subpath_list.at(j));
1718 if (!subpath.isClosed())
1719 buildUp += subpath.first();
1720 if (!buildUp.isClosed())
1721 buildUp += buildUp.constFirst();
1744 if (QtPrivate::fuzzyCompare(y1, y2)) {
1747 }
else if (y2 < y1) {
1748 qreal x_tmp = x2; x2 = x1; x1 = x_tmp;
1749 qreal y_tmp = y2; y2 = y1; y1 = y_tmp;
1753 if (y >= y1 && y < y2) {
1754 qreal x = x1 + ((x2 - x1) / (y2 - y1)) * (y - y1);
1764 int *winding,
int depth = 0)
1768 QRectF bounds = bezier.bounds();
1774 if (y >= bounds.y() && y < bounds.y() + bounds.height()) {
1778 const qreal lower_bound = qreal(.001);
1779 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound)) {
1783 if (bezier.pt1().x() <= x) {
1784 (*winding) += (bezier.pt4().y() > bezier.pt1().y() ? 1 : -1);
1790 const auto halves = bezier.split();
1791 qt_painterpath_isect_curve(halves.first, pt, winding, depth + 1);
1792 qt_painterpath_isect_curve(halves.second, pt, winding, depth + 1);
1797
1798
1799
1800
1801
1802
1803
1804bool QPainterPath::contains(
const QPointF &pt)
const
1806 if (isEmpty() || !controlPointRect().contains(pt))
1809 QPainterPathPrivate *d = d_func();
1811 int winding_number = 0;
1815 for (
int i=0; i<d->elements.size(); ++i) {
1816 const Element &e = d->elements.at(i);
1822 qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number);
1823 last_start = last_pt = e;
1827 qt_painterpath_isect_line(last_pt, e, pt, &winding_number);
1831 case CurveToElement:
1833 const QPainterPath::Element &cp2 = d->elements.at(++i);
1834 const QPainterPath::Element &ep = d->elements.at(++i);
1835 qt_painterpath_isect_curve(QBezier::fromPoints(last_pt, e, cp2, ep),
1836 pt, &winding_number);
1848 if (last_pt != last_start)
1849 qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number);
1851 return (d->hasWindingFill
1852 ? (winding_number != 0)
1853 : ((winding_number % 2) != 0));
1861 qreal left = rect.left();
1862 qreal right = rect.right();
1863 qreal top = rect.top();
1864 qreal bottom = rect.bottom();
1867 int p1 = ((x1 < left) <<
Left)
1868 | ((x1 > right) <<
Right)
1869 | ((y1 < top) <<
Top)
1870 | ((y1 > bottom) <<
Bottom);
1871 int p2 = ((x2 < left) <<
Left)
1872 | ((x2 > right) <<
Right)
1873 | ((y2 < top) <<
Top)
1874 | ((y2 > bottom) <<
Bottom);
1886 y1 += dy/dx * (left - x1);
1888 }
else if (x1 > right) {
1889 y1 -= dy/dx * (x1 - right);
1893 y2 += dy/dx * (left - x2);
1895 }
else if (x2 > right) {
1896 y2 -= dy/dx * (x2 - right);
1900 p1 = ((y1 < top) <<
Top)
1901 | ((y1 > bottom) <<
Bottom);
1902 p2 = ((y2 < top) <<
Top)
1903 | ((y2 > bottom) <<
Bottom);
1910 x1 += dx/dy * (top - y1);
1912 }
else if (y1 > bottom) {
1913 x1 -= dx/dy * (y1 - bottom);
1917 x2 += dx/dy * (top - y2);
1919 }
else if (y2 > bottom) {
1920 x2 -= dx/dy * (y2 - bottom);
1924 p1 = ((x1 < left) <<
Left)
1925 | ((x1 > right) <<
Right);
1926 p2 = ((x2 < left) <<
Left)
1927 | ((x2 > right) <<
Right);
1939 QRectF bounds = bezier.bounds();
1941 if (y >= bounds.top() && y < bounds.bottom()
1942 && bounds.right() >= x1 && bounds.left() < x2) {
1943 const qreal lower_bound = qreal(.01);
1944 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound))
1947 const auto halves = bezier.split();
1948 if (qt_isect_curve_horizontal(halves.first, y, x1, x2, depth + 1)
1949 || qt_isect_curve_horizontal(halves.second, y, x1, x2, depth + 1))
1957 QRectF bounds = bezier.bounds();
1959 if (x >= bounds.left() && x < bounds.right()
1960 && bounds.bottom() >= y1 && bounds.top() < y2) {
1961 const qreal lower_bound = qreal(.01);
1962 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound))
1965 const auto halves = bezier.split();
1966 if (qt_isect_curve_vertical(halves.first, x, y1, y2, depth + 1)
1967 || qt_isect_curve_vertical(halves.second, x, y1, y2, depth + 1))
1975 if ((point.x() == rect.left() || point.x() == rect.right()) &&
1976 (point.y() >= rect.top() && point.y() <= rect.bottom()))
1978 if ((point.y() == rect.top() || point.y() == rect.bottom()) &&
1979 (point.x() >= rect.left() && point.x() <= rect.right()))
1985
1986
1991 enum { OnRect, InsideRect, OutsideRect} edgeStatus = OnRect;
1992 for (
int i=0; i<path->elementCount(); ++i) {
1993 const QPainterPath::Element &e = path->elementAt(i);
1997 case QPainterPath::MoveToElement:
1999 && qFuzzyCompare(last_pt, last_start)
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 Q_ASSERT(totalLength != 0);
2942 if (d->cacheEnabled) {
2943 const int ei = qMax(d->elementAtT(len / totalLength), 1);
2945 const QPainterPath::Element &e = d->elements[ei];
2947 case QPainterPath::LineToElement:
2948 res = len / totalLength;
2950 case CurveToElement:
2952 QBezier b = QBezier::fromPoints(d->elements.at(ei-1),
2954 d->elements.at(ei+1),
2955 d->elements.at(ei+2));
2956 qreal prevLen = d->m_runLengths[ei - 1];
2957 qreal blen = d->m_runLengths[ei] - prevLen;
2958 qreal elemRes = b.tAtLength(len - prevLen);
2959 res = (elemRes * blen + prevLen) / totalLength;
2969 for (
int i=1; i<d->elements.size(); ++i) {
2970 const Element &e = d->elements.at(i);
2977 QLineF line(d->elements.at(i-1), e);
2978 qreal llen = line.length();
2980 if (curLen >= len) {
2981 return len/totalLength ;
2986 case CurveToElement:
2988 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
2990 d->elements.at(i+1),
2991 d->elements.at(i+2));
2992 qreal blen = b.length();
2993 qreal prevLen = curLen;
2996 if (curLen >= len) {
2997 qreal res = b.tAtLength(len - prevLen);
2998 return (res * blen + prevLen)/totalLength;
3013 qreal *bezierLength)
3015 *startingLength = 0;
3020 qreal totalLength = path.length();
3022 const int lastElement = path.elementCount() - 1;
3023 for (
int i=0; i <= lastElement; ++i) {
3024 const QPainterPath::Element &e = path.elementAt(i);
3027 case QPainterPath::MoveToElement:
3029 case QPainterPath::LineToElement:
3031 QLineF line(path.elementAt(i-1), e);
3032 qreal llen = line.length();
3034 if (i == lastElement || curLen/totalLength >= t) {
3035 *bezierLength = llen;
3036 QPointF a = path.elementAt(i-1);
3037 QPointF delta = e - a;
3038 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
3042 case QPainterPath::CurveToElement:
3044 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
3046 path.elementAt(i+1),
3047 path.elementAt(i+2));
3048 qreal blen = b.length();
3051 if (i + 2 == lastElement || curLen/totalLength >= t) {
3052 *bezierLength = blen;
3062 *startingLength = curLen;
3068 qreal *bezierLength)
const
3070 Q_ASSERT(t >= 0 && t <= 1);
3072 if (!path.isEmpty() && d->cacheEnabled) {
3073 const int ei = qMax(d->elementAtT(t), 1);
3074 const qreal prevRunLength = d->m_runLengths[ei - 1];
3075 *startingLength = prevRunLength;
3076 *bezierLength = d->m_runLengths[ei] - prevRunLength;
3077 const QPointF prev = d->elements[ei - 1];
3078 const QPainterPath::Element &e = d->elements[ei];
3080 case QPainterPath::LineToElement:
3082 QPointF delta = (e - prev) / 3;
3083 return QBezier::fromPoints(prev, prev + delta, prev + 2 * delta, e);
3085 case QPainterPath::CurveToElement:
3086 return QBezier::fromPoints(prev, e, elements[ei + 1], elements[ei + 2]);
3093 return uncached_bezierAtT(path, t, startingLength, bezierLength);
3097
3098
3099
3100
3101
3102
3103
3104
3105QPointF QPainterPath::pointAtPercent(qreal t)
const
3107 if (t < 0 || t > 1) {
3108 qWarning(
"QPainterPath::pointAtPercent accepts only values between 0 and 1");
3112 if (!d_ptr || d_ptr->elements.size() == 0)
3115 if (d_ptr->elements.size() == 1)
3116 return d_ptr->elements.at(0);
3118 qreal totalLength = length();
3120 qreal bezierLen = 0;
3121 QBezier b = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3122 Q_ASSERT(bezierLen != 0);
3123 qreal realT = (totalLength * t - curLen) / bezierLen;
3125 return b.pointAt(qBound(qreal(0), realT, qreal(1)));
3129
3130
3131
3132
3133
3134
3135
3136
3137
3138
3139
3140qreal QPainterPath::angleAtPercent(qreal t)
const
3142 if (t < 0 || t > 1) {
3143 qWarning(
"QPainterPath::angleAtPercent accepts only values between 0 and 1");
3150 qreal totalLength = length();
3152 qreal bezierLen = 0;
3153 QBezier bez = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3154 Q_ASSERT(bezierLen != 0);
3155 qreal realT = (totalLength * t - curLen) / bezierLen;
3157 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3158 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3160 return QLineF(0, 0, m1, m2).angle();
3165
3166
3167
3168
3169
3170
3171
3172
3173qreal QPainterPath::slopeAtPercent(qreal t)
const
3175 if (t < 0 || t > 1) {
3176 qWarning(
"QPainterPath::slopeAtPercent accepts only values between 0 and 1");
3183 qreal totalLength = length();
3185 qreal bezierLen = 0;
3186 QBezier bez = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3187 Q_ASSERT(bezierLen != 0);
3188 qreal realT = (totalLength * t - curLen) / bezierLen;
3190 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3191 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3198 if (std::numeric_limits<qreal>::has_infinity) {
3199 slope = (m2 < 0) ? -std::numeric_limits<qreal>::infinity()
3200 : std::numeric_limits<qreal>::infinity();
3202 if (
sizeof(qreal) ==
sizeof(
double)) {
3203 return 1.79769313486231570e+308;
3205 return ((qreal)3.40282346638528860e+38);
3214
3215
3216
3217
3218
3219
3220
3221
3222
3223
3224
3225
3226
3227
3228
3230QPainterPath QPainterPath::trimmed(qreal fromFraction, qreal toFraction, qreal offset)
const
3236 if (!isCachingEnabled()) {
3237 QPainterPath copy(*
this);
3238 copy.setCachingEnabled(
true);
3239 return copy.trimmed(fromFraction, toFraction, offset);
3242 qreal f1 = qBound(qreal(0), fromFraction, qreal(1));
3243 qreal f2 = qBound(qreal(0), toFraction, qreal(1));
3244 if (qFuzzyIsNull(f1 - f2))
3245 return QPainterPath();
3248 if (qFuzzyCompare(f2 - f1, qreal(1)))
3252 res.setFillRule(fillRule());
3256 offset = std::modf(offset, &dummy);
3258 qreal of1 = f1 + offset;
3259 qreal of2 = f2 + offset;
3261 f1 = of1 < 0 ? of1 + 1 : of1;
3262 f2 = of2 + 1 > 1 ? of2 : of2 + 1;
3263 }
else if (offset > 0) {
3264 f1 = of1 - 1 < 0 ? of1 : of1 - 1;
3265 f2 = of2 > 1 ? of2 - 1 : of2;
3268 const bool wrapping = (f1 > f2);
3271 QPainterPathPrivate *d = d_func();
3272 if (d->dirtyRunLengths)
3273 d->computeRunLengths();
3274 const qreal totalLength = d->m_runLengths.last();
3275 if (qFuzzyIsNull(totalLength))
3278 const qreal l1 = f1 * totalLength;
3279 const qreal l2 = f2 * totalLength;
3280 const int e1 = d->elementAtLength(l1);
3281 const bool mustTrimE1 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e1), l1);
3282 const int e2 = d->elementAtLength(l2);
3283 const bool mustTrimE2 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e2), l2);
3286 if (e1 == e2 && !wrapping && mustTrimE1 && mustTrimE2) {
3288 d->appendSliceOfElement(&res, e1, l1, l2);
3292 d->appendEndOfElement(&res, e1, l1);
3294 res.moveTo(d->endPointOfElement(e1));
3297 int firstWholeElement = e1 + 1;
3298 int lastWholeElement = (mustTrimE2 ? e2 - 1 : e2);
3300 d->appendElementRange(&res, firstWholeElement, lastWholeElement);
3302 int lastIndex = d->elements.size() - 1;
3303 d->appendElementRange(&res, firstWholeElement, lastIndex);
3304 bool isClosed = (QPointF(d->elements.at(0)) == QPointF(d->elements.at(lastIndex)));
3306 d->appendElementRange(&res, (isClosed ? 1 : 0), lastWholeElement);
3311 d->appendStartOfElement(&res, e2, l2);
3318 qreal startLen, qreal endLen)
3320 Q_ASSERT(cacheEnabled);
3321 Q_ASSERT(!dirtyRunLengths);
3323 if (elemIdx <= 0 || elemIdx >= elements.size())
3326 const qreal prevLen = m_runLengths.at(elemIdx - 1);
3327 const qreal elemLen = m_runLengths.at(elemIdx) - prevLen;
3328 const qreal len1 = startLen - prevLen;
3329 const qreal len2 = endLen - prevLen;
3330 if (qFuzzyIsNull(elemLen))
3333 const QPointF pp = elements.at(elemIdx - 1);
3334 const QPainterPath::Element e = elements.at(elemIdx);
3337 QPointF p1 = (trimFlags & TrimStart) ? l.pointAt(len1 / elemLen) : pp;
3338 QPointF p2 = (trimFlags & TrimEnd) ? l.pointAt(len2 / elemLen) : e;
3342 }
else if (e.isCurveTo()) {
3343 Q_ASSERT(elemIdx < elements.size() - 2);
3344 QBezier b = QBezier::fromPoints(pp, e, elements.at(elemIdx + 1), elements.at(elemIdx + 2));
3345 qreal t1 = (trimFlags & TrimStart) ? b.tAtLength(len1) : 0.0;
3346 qreal t2 = (trimFlags & TrimEnd) ? b.tAtLength(len2) : 1.0;
3347 QBezier c = b.getSubRange(t1, t2);
3349 to->moveTo(c.pt1());
3350 to->cubicTo(c.pt2(), c.pt3(), c.pt4());
3358 if (first < 0 || first >= elements.size() || last < 0 || last >= elements.size())
3362 for (
int i = first; i <= last; i++) {
3363 const QPainterPath::Element &e = elements.at(i);
3365 case QPainterPath::MoveToElement:
3368 case QPainterPath::LineToElement:
3371 case QPainterPath::CurveToElement:
3372 Q_ASSERT(i < elements.size() - 2);
3373 to->cubicTo(e, elements.at(i + 1), elements.at(i + 2));
3385
3386
3387
3388
3389
3390
3391
3392
3393
3394
3395
3396
3397void QPainterPath::addRoundedRect(
const QRectF &rect, qreal xRadius, qreal yRadius,
3400 QRectF r = rect.normalized();
3405 if (mode == Qt::AbsoluteSize) {
3406 qreal w = r.width() / 2;
3407 qreal h = r.height() / 2;
3412 xRadius = 100 * qMin(xRadius, w) / w;
3417 yRadius = 100 * qMin(yRadius, h) / h;
3427 if (xRadius <= 0 || yRadius <= 0) {
3434 qreal w = r.width();
3435 qreal h = r.height();
3436 qreal rxx2 = w*xRadius/100;
3437 qreal ryy2 = h*yRadius/100;
3442 bool first = d_func()->elements.size() < 2;
3444 arcMoveTo(x, y, rxx2, ryy2, 180);
3445 arcTo(x, y, rxx2, ryy2, 180, -90);
3446 arcTo(x+w-rxx2, y, rxx2, ryy2, 90, -90);
3447 arcTo(x+w-rxx2, y+h-ryy2, rxx2, ryy2, 0, -90);
3448 arcTo(x, y+h-ryy2, rxx2, ryy2, 270, -90);
3451 d_func()->require_moveTo =
true;
3452 d_func()->convex = first;
3456
3457
3458
3459
3460
3461
3464
3465
3466
3467
3468
3469
3470
3471
3472
3473
3474
3475QPainterPath QPainterPath::united(
const QPainterPath &p)
const
3477 if (isEmpty() || p.isEmpty())
3478 return isEmpty() ? p : *
this;
3479 QPathClipper clipper(*
this, p);
3480 return clipper.clip(QPathClipper::BoolOr);
3484
3485
3486
3487
3488
3489
3490QPainterPath QPainterPath::intersected(
const QPainterPath &p)
const
3492 if (isEmpty() || p.isEmpty())
3493 return QPainterPath();
3494 QPathClipper clipper(*
this, p);
3495 return clipper.clip(QPathClipper::BoolAnd);
3499
3500
3501
3502
3503
3504
3505
3506
3507
3508QPainterPath QPainterPath::subtracted(
const QPainterPath &p)
const
3510 if (isEmpty() || p.isEmpty())
3512 QPathClipper clipper(*
this, p);
3513 return clipper.clip(QPathClipper::BoolSub);
3517
3518
3519
3520
3521
3522
3523
3524
3525QPainterPath QPainterPath::simplified()
const
3529 QPathClipper clipper(*
this, QPainterPath());
3530 return clipper.clip(QPathClipper::Simplify);
3534
3535
3536
3537
3538
3539
3540
3541
3542
3543
3544bool QPainterPath::intersects(
const QPainterPath &p)
const
3546 if (p.elementCount() == 1)
3547 return contains(p.elementAt(0));
3548 if (isEmpty() || p.isEmpty())
3550 QPathClipper clipper(*
this, p);
3551 return clipper.intersect();
3555
3556
3557
3558
3559
3560
3561
3562
3563
3564
3565
3566bool QPainterPath::contains(
const QPainterPath &p)
const
3568 if (p.elementCount() == 1)
3569 return contains(p.elementAt(0));
3570 if (isEmpty() || p.isEmpty())
3572 QPathClipper clipper(*
this, p);
3573 return clipper.contains();
3576void QPainterPath::setDirty(
bool dirty)
3578 d_func()->pathConverter.reset();
3579 d_func()->dirtyBounds = dirty;
3580 d_func()->dirtyControlBounds = dirty;
3581 d_func()->dirtyRunLengths = dirty;
3582 d_func()->convex =
false;
3585void QPainterPath::computeBoundingRect()
const
3587 QPainterPathPrivate *d = d_func();
3588 d->dirtyBounds =
false;
3590 d->bounds = QRect();
3594 qreal minx, maxx, miny, maxy;
3595 minx = maxx = d->elements.at(0).x;
3596 miny = maxy = d->elements.at(0).y;
3597 for (
int i=1; i<d->elements.size(); ++i) {
3598 const Element &e = d->elements.at(i);
3603 if (e.x > maxx) maxx = e.x;
3604 else if (e.x < minx) minx = e.x;
3605 if (e.y > maxy) maxy = e.y;
3606 else if (e.y < miny) miny = e.y;
3608 case CurveToElement:
3610 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
3612 d->elements.at(i+1),
3613 d->elements.at(i+2));
3614 QRectF r = qt_painterpath_bezier_extrema(b);
3615 qreal right = r.right();
3616 qreal bottom = r.bottom();
3617 if (r.x() < minx) minx = r.x();
3618 if (right > maxx) maxx = right;
3619 if (r.y() < miny) miny = r.y();
3620 if (bottom > maxy) maxy = bottom;
3628 d->bounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3632void QPainterPath::computeControlPointRect()
const
3634 QPainterPathPrivate *d = d_func();
3635 d->dirtyControlBounds =
false;
3637 d->controlBounds = QRect();
3641 qreal minx, maxx, miny, maxy;
3642 minx = maxx = d->elements.at(0).x;
3643 miny = maxy = d->elements.at(0).y;
3644 for (
int i=1; i<d->elements.size(); ++i) {
3645 const Element &e = d->elements.at(i);
3646 if (e.x > maxx) maxx = e.x;
3647 else if (e.x < minx) minx = e.x;
3648 if (e.y > maxy) maxy = e.y;
3649 else if (e.y < miny) miny = e.y;
3651 d->controlBounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3656 Q_ASSERT(!elements.isEmpty());
3658 m_runLengths.clear();
3659 const int numElems = elements.size();
3660 m_runLengths.reserve(numElems);
3662 QPointF runPt = elements[0];
3664 for (
int i = 0; i < numElems; i++) {
3665 QPainterPath::Element e = elements[i];
3667 case QPainterPath::LineToElement:
3668 runLen += QLineF(runPt, e).length();
3671 case QPainterPath::CurveToElement: {
3672 Q_ASSERT(i < numElems - 2);
3673 QPainterPath::Element ee = elements[i + 2];
3674 runLen += QBezier::fromPoints(runPt, e, elements[i + 1], ee).length();
3678 case QPainterPath::MoveToElement:
3681 case QPainterPath::CurveToDataElement:
3684 m_runLengths.append(runLen);
3686 Q_ASSERT(m_runLengths.size() == elements.size());
3688 dirtyRunLengths =
false;
3691#ifndef QT_NO_DEBUG_STREAM
3694 QDebugStateSaver saver(s);
3695 s.nospace() <<
"QPainterPath: Element count=" << p.elementCount() << Qt::endl;
3696 const char *types[] = {
"MoveTo",
"LineTo",
"CurveTo",
"CurveToData"};
3697 for (
int i=0; i<p.elementCount(); ++i) {
3698 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)