14#include <qtextlayout.h>
15#include <qvarlengtharray.h>
18#include <private/qbezier_p.h>
19#include <private/qfontengine_p.h>
20#include <private/qnumeric_p.h>
21#include <private/qobject_p.h>
22#include <private/qpathclipper_p.h>
23#include <private/qstroker_p.h>
24#include <private/qtextengine_p.h>
31#include <performance.h>
42 if (
sizeof(qreal) >=
sizeof(
double))
43 return qIsFinite(c) && fabs(c) < 1e128;
45 return qIsFinite(c) && fabsf(
float(c)) < 1e16f;
50 return isValidCoord(p.x()) && isValidCoord(p.y());
55 return isValidCoord(r.x()) && isValidCoord(r.y()) && isValidCoord(r.width()) && isValidCoord(r.height());
69 QPointF* startPoint, QPointF *endPoint)
73 *startPoint = QPointF();
75 *endPoint = QPointF();
79 qreal w2 = r.width() / 2;
80 qreal h2 = r.height() / 2;
82 qreal angles[2] = { angle, angle + length };
83 QPointF *points[2] = { startPoint, endPoint };
85 for (
int i = 0; i < 2; ++i) {
89 qreal theta = angles[i] - 360 *
std::floor(angles[i] / 360);
92 int quadrant =
int(t);
95 t = qt_t_for_arc_angle(90 * t);
102 QBezier::coefficients(t, a, b, c, d);
103 QPointF p(a + b + c*QT_PATH_KAPPA, d + c + b*QT_PATH_KAPPA);
106 if (quadrant == 1 || quadrant == 2)
110 if (quadrant == 0 || quadrant == 1)
113 *points[i] = r.center() + QPointF(w2 * p.x(), h2 * p.y());
118static void qt_debug_path(
const QPainterPath &path)
120 const char *names[] = {
127 printf(
"\nQPainterPath: elementCount=%d\n", path.elementCount());
128 for (
int i=0; i<path.elementCount(); ++i) {
129 const QPainterPath::Element &e = path.elementAt(i);
130 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
131 printf(
" - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
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)
974 if (!hasValidCoords(rect) || !isValidCoord(angle)) {
976 qWarning(
"QPainterPath::arcMoveTo: Adding point with invalid coordinates, ignoring call");
985 qt_find_ellipse_coords(rect, angle, 0, &pt,
nullptr);
992
993
994
995
996QPointF QPainterPath::currentPosition()
const
998 return !d_ptr || d_func()->elements.isEmpty()
1000 : QPointF(d_func()->elements.constLast().x, d_func()->elements.constLast().y);
1005
1006
1007
1008
1009
1010
1011
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032void QPainterPath::addRect(
const QRectF &r)
1034 if (!hasValidCoords(r)) {
1036 qWarning(
"QPainterPath::addRect: Adding point with invalid coordinates, ignoring call");
1047 bool first = d_func()->elements.size() < 2;
1049 moveTo(r.x(), r.y());
1051 Element l1 = { r.x() + r.width(), r.y(), LineToElement };
1052 Element l2 = { r.x() + r.width(), r.y() + r.height(), LineToElement };
1053 Element l3 = { r.x(), r.y() + r.height(), LineToElement };
1054 Element l4 = { r.x(), r.y(), LineToElement };
1056 d_func()->elements << l1 << l2 << l3 << l4;
1057 d_func()->require_moveTo =
true;
1058 d_func()->convex = first;
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078void QPainterPath::addPolygon(
const QPolygonF &polygon)
1080 if (polygon.isEmpty())
1086 moveTo(polygon.constFirst());
1087 for (
int i=1; i<polygon.size(); ++i) {
1088 Element elm = { polygon.at(i).x(), polygon.at(i).y(), LineToElement };
1089 d_func()->elements << elm;
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112void QPainterPath::addEllipse(
const QRectF &boundingRect)
1114 if (!hasValidCoords(boundingRect)) {
1116 qWarning(
"QPainterPath::addEllipse: Adding point with invalid coordinates, ignoring call");
1121 if (boundingRect.isNull())
1127 bool first = d_func()->elements.size() < 2;
1131 QPointF start = qt_curves_for_arc(boundingRect, 0, -360, pts, &point_count);
1134 cubicTo(pts[0], pts[1], pts[2]);
1135 cubicTo(pts[3], pts[4], pts[5]);
1136 cubicTo(pts[6], pts[7], pts[8]);
1137 cubicTo(pts[9], pts[10], pts[11]);
1138 d_func()->require_moveTo =
true;
1140 d_func()->convex = first;
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164void QPainterPath::addText(
const QPointF &point,
const QFont &f,
const QString &text)
1172 QTextLayout layout(text, f);
1173 layout.setCacheEnabled(
true);
1175 QTextOption opt = layout.textOption();
1176 opt.setUseDesignMetrics(
true);
1177 layout.setTextOption(opt);
1179 QTextEngine *eng = layout.engine();
1180 layout.beginLayout();
1181 QTextLine line = layout.createLine();
1184 const QScriptLine &sl = eng->lines[0];
1185 if (!sl.length || !eng->layoutData)
1188 int nItems = eng->layoutData->items.size();
1193 QVarLengthArray<
int> visualOrder(nItems);
1194 QVarLengthArray<uchar> levels(nItems);
1195 for (
int i = 0; i < nItems; ++i)
1196 levels[i] = eng->layoutData->items.at(i).analysis.bidiLevel;
1197 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
1199 for (
int i = 0; i < nItems; ++i) {
1200 int item = visualOrder[i];
1201 const QScriptItem &si = eng->layoutData->items.at(item);
1203 if (si.analysis.flags < QScriptAnalysis::TabOrObject) {
1204 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
1205 QFontEngine *fe = eng->fontEngine(si);
1207 fe->addOutlineToPath(x, y, glyphs,
this,
1208 si.analysis.bidiLevel % 2
1209 ? QTextItem::RenderFlags(QTextItem::RightToLeft)
1210 : QTextItem::RenderFlags{});
1212 const qreal lw = fe->lineThickness().toReal();
1213 if (f.d->underline) {
1214 qreal pos = fe->underlinePosition().toReal();
1215 addRect(x, y + pos, si.width.toReal(), lw);
1217 if (f.d->overline) {
1218 qreal pos = fe->ascent().toReal() + 1;
1219 addRect(x, y - pos, si.width.toReal(), lw);
1221 if (f.d->strikeOut) {
1222 qreal pos = fe->ascent().toReal() / 3;
1223 addRect(x, y - pos, si.width.toReal(), lw);
1226 x += si.width.toReal();
1231
1232
1233
1234
1235
1236
1237
1238void QPainterPath::addPath(
const QPainterPath &other)
1240 if (other.isEmpty())
1246 QPainterPathPrivate *d = d_func();
1248 if (d->elements.constLast().type == MoveToElement)
1249 d->elements.remove(d->elements.size()-1);
1252 int cStart = d->elements.size() + other.d_func()->cStart;
1253 d->elements += other.d_func()->elements;
1256 d->require_moveTo = other.d_func()->isClosed();
1261
1262
1263
1264
1265
1266
1267
1268
1269void QPainterPath::connectPath(
const QPainterPath &other)
1271 if (other.isEmpty())
1277 QPainterPathPrivate *d = d_func();
1279 if (d->elements.constLast().type == MoveToElement)
1280 d->elements.remove(d->elements.size()-1);
1283 int cStart = d->elements.size() + other.d_func()->cStart;
1284 int first = d->elements.size();
1285 d->elements += other.d_func()->elements;
1288 d->elements[first].type = LineToElement;
1291 if (first > 0 && QPointF(d->elements.at(first)) == QPointF(d->elements.at(first - 1))) {
1292 d->elements.remove(first--);
1296 if (cStart != first)
1301
1302
1303
1304
1305
1306
1307void QPainterPath::addRegion(
const QRegion ®ion)
1312 for (
const QRect &rect : region)
1318
1319
1320
1321
1322Qt::FillRule QPainterPath::fillRule()
const
1324 return d_func() && d_func()->hasWindingFill ? Qt::WindingFill : Qt::OddEvenFill;
1328
1329
1330
1331
1332
1333
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344void QPainterPath::setFillRule(Qt::FillRule fillRule)
1347 const bool isWindingRequested = (fillRule == Qt::WindingFill);
1348 if (d_func()->hasWindingFill == isWindingRequested)
1352 d_func()->hasWindingFill = isWindingRequested;
1355#define QT_BEZIER_A(bezier, coord) 3
* (-bezier.coord##1
1360#define QT_BEZIER_B(bezier, coord) 6
* (bezier.coord##1
1364#define QT_BEZIER_C(bezier, coord) 3
* (- bezier.coord##1
1367#define QT_BEZIER_CHECK_T(bezier, t)
1368 if (t >= 0
&& t <= 1
) {
1369 QPointF p(b.pointAt(t));
1370 if (p.x() < minx) minx = p.x();
1371 else if (p.x() > maxx) maxx = p.x();
1372 if (p.y() < miny) miny = p.y();
1373 else if (p.y() > maxy) maxy = p.y();
1379 qreal minx, miny, maxx, maxy;
1403 if (qFuzzyIsNull(ax)) {
1406 if (!qFuzzyIsNull(bx)) {
1412 const qreal tx = bx * bx - 4 * ax * cx;
1415 qreal temp = qSqrt(tx);
1416 qreal rcp = 1 / (2 * ax);
1417 qreal t1 = (-bx + temp) * rcp;
1420 qreal t2 = (-bx - temp) * rcp;
1433 if (qFuzzyIsNull(ay)) {
1436 if (!qFuzzyIsNull(by)) {
1442 const qreal ty = by * by - 4 * ay * cy;
1445 qreal temp = qSqrt(ty);
1446 qreal rcp = 1 / (2 * ay);
1447 qreal t1 = (-by + temp) * rcp;
1450 qreal t2 = (-by - temp) * rcp;
1455 return QRectF(minx, miny, maxx - minx, maxy - miny);
1459
1460
1461
1462
1463
1464QRectF QPainterPath::boundingRect()
const
1468 QPainterPathPrivate *d = d_func();
1471 computeBoundingRect();
1476
1477
1478
1479
1480
1481
1482
1483
1484
1485QRectF QPainterPath::controlPointRect()
const
1489 QPainterPathPrivate *d = d_func();
1491 if (d->dirtyControlBounds)
1492 computeControlPointRect();
1493 return d->controlBounds;
1498
1499
1500
1501
1502
1503
1504
1506bool QPainterPath::isEmpty()
const
1508 return !d_ptr || (d_ptr->elements.size() == 1 && d_ptr->elements.constFirst().type == MoveToElement);
1512
1513
1514
1515
1516
1517
1518
1519QPainterPath QPainterPath::toReversed()
const
1521 Q_D(
const QPainterPath);
1529 rev.moveTo(d->elements.at(d->elements.size()-1).x, d->elements.at(d->elements.size()-1).y);
1531 for (
int i=d->elements.size()-1; i>=1; --i) {
1532 const QPainterPath::Element &elm = d->elements.at(i);
1533 const QPainterPath::Element &prev = d->elements.at(i-1);
1536 rev.lineTo(prev.x, prev.y);
1539 rev.moveTo(prev.x, prev.y);
1541 case CurveToDataElement:
1544 const QPainterPath::Element &cp1 = d->elements.at(i-2);
1545 const QPainterPath::Element &sp = d->elements.at(i-3);
1546 Q_ASSERT(prev.type == CurveToDataElement);
1547 Q_ASSERT(cp1.type == CurveToElement);
1548 rev.cubicTo(prev.x, prev.y, cp1.x, cp1.y, sp.x, sp.y);
1553 Q_ASSERT(!
"qt_reversed_path");
1562
1563
1564
1565
1566
1567
1568
1569
1570
1571
1572
1573QList<QPolygonF> QPainterPath::toSubpathPolygons(
const QTransform &matrix)
const
1576 Q_D(
const QPainterPath);
1577 QList<QPolygonF> flatCurves;
1582 for (
int i=0; i<elementCount(); ++i) {
1583 const QPainterPath::Element &e = d->elements.at(i);
1585 case QPainterPath::MoveToElement:
1586 if (current.size() > 1)
1587 flatCurves += current;
1589 current.reserve(16);
1590 current += QPointF(e.x, e.y) * matrix;
1592 case QPainterPath::LineToElement:
1593 current += QPointF(e.x, e.y) * matrix;
1595 case QPainterPath::CurveToElement: {
1596 Q_ASSERT(d->elements.at(i+1).type == QPainterPath::CurveToDataElement);
1597 Q_ASSERT(d->elements.at(i+2).type == QPainterPath::CurveToDataElement);
1598 QBezier bezier = QBezier::fromPoints(QPointF(d->elements.at(i-1).x, d->elements.at(i-1).y) * matrix,
1599 QPointF(e.x, e.y) * matrix,
1600 QPointF(d->elements.at(i+1).x, d->elements.at(i+1).y) * matrix,
1601 QPointF(d->elements.at(i+2).x, d->elements.at(i+2).y) * matrix);
1602 bezier.addToPolygon(¤t);
1606 case QPainterPath::CurveToDataElement:
1607 Q_ASSERT(!
"QPainterPath::toSubpathPolygons(), bad element type");
1612 if (current.size()>1)
1613 flatCurves += current;
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640QList<QPolygonF> QPainterPath::toFillPolygons(
const QTransform &matrix)
const
1643 QList<QPolygonF> polys;
1645 QList<QPolygonF> subpaths = toSubpathPolygons(matrix);
1646 int count = subpaths.size();
1651 QList<QRectF> bounds;
1652 bounds.reserve(count);
1653 for (
int i=0; i<count; ++i)
1654 bounds += subpaths.at(i).boundingRect();
1656#ifdef QPP_FILLPOLYGONS_DEBUG
1657 printf(
"QPainterPath::toFillPolygons, subpathCount=%d\n", count);
1658 for (
int i=0; i<bounds.size(); ++i)
1659 qDebug() <<
" bounds" << i << bounds.at(i);
1662 QList< QList<
int> > isects;
1663 isects.resize(count);
1666 for (
int j=0; j<count; ++j) {
1667 if (subpaths.at(j).size() <= 2)
1669 QRectF cbounds = bounds.at(j);
1670 for (
int i=0; i<count; ++i) {
1671 if (cbounds.intersects(bounds.at(i))) {
1677#ifdef QPP_FILLPOLYGONS_DEBUG
1678 printf(
"Intersections before flattening:\n");
1679 for (
int i = 0; i < count; ++i) {
1681 for (
int j = 0; j < isects[i].size(); ++j) {
1682 printf(
"%d ", isects[i][j]);
1689 for (
int i=0; i<count; ++i) {
1690 const QList<
int> ¤t_isects = isects.at(i);
1691 for (
int j=0; j<current_isects.size(); ++j) {
1692 int isect_j = current_isects.at(j);
1695 const QList<
int> &isects_j = isects.at(isect_j);
1696 for (
int k = 0, size = isects_j.size(); k < size; ++k) {
1697 int isect_k = isects_j.at(k);
1698 if (isect_k != i && !isects.at(i).contains(isect_k)) {
1699 isects[i] += isect_k;
1702 isects[isect_j].clear();
1706#ifdef QPP_FILLPOLYGONS_DEBUG
1707 printf(
"Intersections after flattening:\n");
1708 for (
int i = 0; i < count; ++i) {
1710 for (
int j = 0; j < isects[i].size(); ++j) {
1711 printf(
"%d ", isects[i][j]);
1718 for (
int i=0; i<count; ++i) {
1719 const QList<
int> &subpath_list = isects.at(i);
1720 if (!subpath_list.isEmpty()) {
1722 for (
int j=0; j<subpath_list.size(); ++j) {
1723 const QPolygonF &subpath = subpaths.at(subpath_list.at(j));
1725 if (!subpath.isClosed())
1726 buildUp += subpath.first();
1727 if (!buildUp.isClosed())
1728 buildUp += buildUp.constFirst();
1751 if (QtPrivate::fuzzyCompare(y1, y2)) {
1754 }
else if (y2 < y1) {
1755 qreal x_tmp = x2; x2 = x1; x1 = x_tmp;
1756 qreal y_tmp = y2; y2 = y1; y1 = y_tmp;
1760 if (y >= y1 && y < y2) {
1761 qreal x = x1 + ((x2 - x1) / (y2 - y1)) * (y - y1);
1771 int *winding,
int depth = 0)
1775 QRectF bounds = bezier.bounds();
1781 if (y >= bounds.y() && y < bounds.y() + bounds.height()) {
1785 const qreal lower_bound = qreal(.001);
1786 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound)) {
1790 if (bezier.pt1().x() <= x) {
1791 (*winding) += (bezier.pt4().y() > bezier.pt1().y() ? 1 : -1);
1797 const auto halves = bezier.split();
1798 qt_painterpath_isect_curve(halves.first, pt, winding, depth + 1);
1799 qt_painterpath_isect_curve(halves.second, pt, winding, depth + 1);
1804
1805
1806
1807
1808
1809
1810
1811bool QPainterPath::contains(
const QPointF &pt)
const
1813 if (isEmpty() || !controlPointRect().contains(pt))
1816 QPainterPathPrivate *d = d_func();
1818 int winding_number = 0;
1822 for (
int i=0; i<d->elements.size(); ++i) {
1823 const Element &e = d->elements.at(i);
1829 qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number);
1830 last_start = last_pt = e;
1834 qt_painterpath_isect_line(last_pt, e, pt, &winding_number);
1838 case CurveToElement:
1840 const QPainterPath::Element &cp2 = d->elements.at(++i);
1841 const QPainterPath::Element &ep = d->elements.at(++i);
1842 qt_painterpath_isect_curve(QBezier::fromPoints(last_pt, e, cp2, ep),
1843 pt, &winding_number);
1855 if (last_pt != last_start)
1856 qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number);
1858 return (d->hasWindingFill
1859 ? (winding_number != 0)
1860 : ((winding_number % 2) != 0));
1868 qreal left = rect.left();
1869 qreal right = rect.right();
1870 qreal top = rect.top();
1871 qreal bottom = rect.bottom();
1874 int p1 = ((x1 < left) <<
Left)
1875 | ((x1 > right) <<
Right)
1876 | ((y1 < top) <<
Top)
1877 | ((y1 > bottom) <<
Bottom);
1878 int p2 = ((x2 < left) <<
Left)
1879 | ((x2 > right) <<
Right)
1880 | ((y2 < top) <<
Top)
1881 | ((y2 > bottom) <<
Bottom);
1893 y1 += dy/dx * (left - x1);
1895 }
else if (x1 > right) {
1896 y1 -= dy/dx * (x1 - right);
1900 y2 += dy/dx * (left - x2);
1902 }
else if (x2 > right) {
1903 y2 -= dy/dx * (x2 - right);
1907 p1 = ((y1 < top) <<
Top)
1908 | ((y1 > bottom) <<
Bottom);
1909 p2 = ((y2 < top) <<
Top)
1910 | ((y2 > bottom) <<
Bottom);
1917 x1 += dx/dy * (top - y1);
1919 }
else if (y1 > bottom) {
1920 x1 -= dx/dy * (y1 - bottom);
1924 x2 += dx/dy * (top - y2);
1926 }
else if (y2 > bottom) {
1927 x2 -= dx/dy * (y2 - bottom);
1931 p1 = ((x1 < left) <<
Left)
1932 | ((x1 > right) <<
Right);
1933 p2 = ((x2 < left) <<
Left)
1934 | ((x2 > right) <<
Right);
1946 QRectF bounds = bezier.bounds();
1948 if (y >= bounds.top() && y < bounds.bottom()
1949 && bounds.right() >= x1 && bounds.left() < x2) {
1950 const qreal lower_bound = qreal(.01);
1951 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound))
1954 const auto halves = bezier.split();
1955 if (qt_isect_curve_horizontal(halves.first, y, x1, x2, depth + 1)
1956 || qt_isect_curve_horizontal(halves.second, y, x1, x2, depth + 1))
1964 QRectF bounds = bezier.bounds();
1966 if (x >= bounds.left() && x < bounds.right()
1967 && bounds.bottom() >= y1 && bounds.top() < y2) {
1968 const qreal lower_bound = qreal(.01);
1969 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound))
1972 const auto halves = bezier.split();
1973 if (qt_isect_curve_vertical(halves.first, x, y1, y2, depth + 1)
1974 || qt_isect_curve_vertical(halves.second, x, y1, y2, depth + 1))
1982 if ((point.x() == rect.left() || point.x() == rect.right()) &&
1983 (point.y() >= rect.top() && point.y() <= rect.bottom()))
1985 if ((point.y() == rect.top() || point.y() == rect.bottom()) &&
1986 (point.x() >= rect.left() && point.x() <= rect.right()))
1992
1993
1998 enum { OnRect, InsideRect, OutsideRect} edgeStatus = OnRect;
1999 for (
int i=0; i<path->elementCount(); ++i) {
2000 const QPainterPath::Element &e = path->elementAt(i);
2004 case QPainterPath::MoveToElement:
2006 && qFuzzyCompare(last_pt, last_start)
2007 && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
2008 last_start.x(), last_start.y(), rect))
2010 last_start = last_pt = e;
2013 case QPainterPath::LineToElement:
2014 if (qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(), e.x, e.y, rect))
2019 case QPainterPath::CurveToElement:
2021 QPointF cp2 = path->elementAt(++i);
2022 QPointF ep = path->elementAt(++i);
2023 QBezier bezier = QBezier::fromPoints(last_pt, e, cp2, ep);
2024 if (qt_isect_curve_horizontal(bezier, rect.top(), rect.left(), rect.right())
2025 || qt_isect_curve_horizontal(bezier, rect.bottom(), rect.left(), rect.right())
2026 || qt_isect_curve_vertical(bezier, rect.left(), rect.top(), rect.bottom())
2027 || qt_isect_curve_vertical(bezier, rect.right(), rect.top(), rect.bottom()))
2038 if (!pointOnEdge(rect, last_pt)) {
2039 bool contained = rect.contains(last_pt);
2040 switch (edgeStatus) {
2050 edgeStatus = contained ? InsideRect : OutsideRect;
2054 if (last_pt == last_start)
2055 edgeStatus = OnRect;
2060 if (last_pt != last_start
2061 && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
2062 last_start.x(), last_start.y(), rect))
2069
2070
2071
2072
2073
2074
2075
2076
2077
2078
2079
2080
2081
2082bool QPainterPath::intersects(
const QRectF &rect)
const
2084 if (elementCount() == 1 && rect.contains(elementAt(0)))
2090 QRectF cp = controlPointRect();
2091 QRectF rn = rect.normalized();
2096 if (qMax(rn.left(), cp.left()) > qMin(rn.right(), cp.right())
2097 || qMax(rn.top(), cp.top()) > qMin(rn.bottom(), cp.bottom()))
2101 if (qt_painterpath_check_crossing(
this, rect))
2104 if (contains(rect.center()))
2110 for (
int i=0; i<d->elements.size(); ++i) {
2111 const Element &e = d->elements.at(i);
2112 if (e.type == QPainterPath::MoveToElement && rect.contains(e))
2120
2121
2122
2123
2124
2125void QPainterPath::translate(qreal dx, qreal dy)
2127 if (!d_ptr || (dx == 0 && dy == 0))
2130 int elementsLeft = d_ptr->elements.size();
2131 if (elementsLeft <= 0)
2135 QPainterPath::Element *element = d_func()->elements.data();
2137 while (elementsLeft--) {
2145
2146
2147
2148
2149
2150
2151
2152
2155
2156
2157
2158
2159
2160QPainterPath QPainterPath::translated(qreal dx, qreal dy)
const
2162 QPainterPath copy(*
this);
2163 copy.translate(dx, dy);
2168
2169
2170
2171
2172
2173
2174
2175
2178
2179
2180
2181
2182
2183bool QPainterPath::contains(
const QRectF &rect)
const
2189 if (isEmpty() || !controlPointRect().contains(rect))
2195 if (qt_painterpath_check_crossing(
this, rect)) {
2196 if (fillRule() == Qt::OddEvenFill) {
2201 if (!contains(rect.topLeft()) ||
2202 !contains(rect.topRight()) ||
2203 !contains(rect.bottomRight()) ||
2204 !contains(rect.bottomLeft()))
2213 if (!contains(rect.center()))
2222 for (
int i=0; i<d->elements.size(); ++i) {
2223 const Element &e = d->elements.at(i);
2224 if (e.type == QPainterPath::MoveToElement && rect.contains(e)) {
2225 if (fillRule() == Qt::OddEvenFill)
2229 for (; !stop && i<d->elements.size(); ++i) {
2230 const Element &el = d->elements.at(i);
2239 case CurveToElement:
2240 if (!contains(d->elements.at(i+2)))
2257static inline bool epsilonCompare(
const QPointF &a,
const QPointF &b,
const QSizeF &epsilon)
2259 return qAbs(a.x() - b.x()) <= epsilon.width()
2260 && qAbs(a.y() - b.y()) <= epsilon.height();
2264
2265
2266
2267
2268
2269
2270
2272bool QPainterPath::operator==(
const QPainterPath &path)
const
2274 QPainterPathPrivate *d = d_func();
2275 QPainterPathPrivate *other_d = path.d_func();
2278 }
else if (!d || !other_d) {
2279 if (!other_d && isEmpty() && elementAt(0) == QPointF() && !d->hasWindingFill)
2281 if (!d && path.isEmpty() && path.elementAt(0) == QPointF() && !other_d->hasWindingFill)
2285 else if (d->hasWindingFill != other_d->hasWindingFill)
2287 else if (d->elements.size() != other_d->elements.size())
2290 const qreal qt_epsilon =
sizeof(qreal) ==
sizeof(
double) ? 1e-12 : qreal(1e-5);
2292 QSizeF epsilon = boundingRect().size();
2293 epsilon.rwidth() *= qt_epsilon;
2294 epsilon.rheight() *= qt_epsilon;
2296 for (
int i = 0; i < d->elements.size(); ++i)
2297 if (d->elements.at(i).type != other_d->elements.at(i).type
2298 || !epsilonCompare(d->elements.at(i), other_d->elements.at(i), epsilon))
2305
2306
2307
2308
2309
2310
2311
2313bool QPainterPath::operator!=(
const QPainterPath &path)
const
2315 return !(*
this==path);
2319
2320
2321
2322
2323
2324
2325QPainterPath QPainterPath::operator&(
const QPainterPath &other)
const
2327 return intersected(other);
2331
2332
2333
2334
2335
2336
2337QPainterPath QPainterPath::operator|(
const QPainterPath &other)
const
2339 return united(other);
2343
2344
2345
2346
2347
2348
2349
2350QPainterPath QPainterPath::operator+(
const QPainterPath &other)
const
2352 return united(other);
2356
2357
2358
2359
2360
2361
2362QPainterPath QPainterPath::operator-(
const QPainterPath &other)
const
2364 return subtracted(other);
2368
2369
2370
2371
2372
2373
2374QPainterPath &QPainterPath::operator&=(
const QPainterPath &other)
2376 return *
this = (*
this & other);
2380
2381
2382
2383
2384
2385
2386QPainterPath &QPainterPath::operator|=(
const QPainterPath &other)
2388 return *
this = (*
this | other);
2392
2393
2394
2395
2396
2397
2398
2399QPainterPath &QPainterPath::operator+=(
const QPainterPath &other)
2401 return *
this = (*
this + other);
2405
2406
2407
2408
2409
2410
2411
2412QPainterPath &QPainterPath::operator-=(
const QPainterPath &other)
2414 return *
this = (*
this - other);
2417#ifndef QT_NO_DATASTREAM
2419
2420
2421
2422
2423
2424
2425
2426
2434 s << p.elementCount();
2435 for (
int i=0; i < p.d_func()->elements.size(); ++i) {
2436 const QPainterPath::Element &e = p.d_func()->elements.at(i);
2438 s <<
double(e.x) <<
double(e.y);
2440 s << p.d_func()->cStart;
2441 s <<
int(p.fillRule());
2446
2447
2448
2449
2450
2451
2452
2453
2456 bool errorDetected =
false;
2467 p.d_func()->elements.clear();
2468 for (
int i=0; i<size; ++i) {
2474 Q_ASSERT(type >= 0 && type <= 3);
2475 if (!isValidCoord(qreal(x)) || !isValidCoord(qreal(y))) {
2477 qWarning(
"QDataStream::operator>>: Invalid QPainterPath coordinates read, skipping it");
2479 errorDetected =
true;
2482 QPainterPath::Element elm = { qreal(x), qreal(y), QPainterPath::ElementType(type) };
2483 p.d_func()->elements.append(elm);
2485 s >> p.d_func()->cStart;
2488 Q_ASSERT(fillRule == Qt::OddEvenFill || fillRule == Qt::WindingFill);
2489 p.d_func()->hasWindingFill = (Qt::FillRule(fillRule) == Qt::WindingFill);
2490 if (errorDetected || p.d_func()->elements.isEmpty())
2498
2499
2503 ((QPainterPath *) data)->moveTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
2508 ((QPainterPath *) data)->lineTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
2512 qfixed c2x, qfixed c2y,
2513 qfixed ex, qfixed ey,
2516 ((QPainterPath *) data)->cubicTo(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y),
2517 qt_fixed_to_real(c2x), qt_fixed_to_real(c2y),
2518 qt_fixed_to_real(ex), qt_fixed_to_real(ey));
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
2564
2565
2566
2567
2568
2569
2570
2575 stroker.setMoveToHook(qt_path_stroke_move_to);
2576 stroker.setLineToHook(qt_path_stroke_line_to);
2577 stroker.setCubicToHook(qt_path_stroke_cubic_to);
2581
2582
2583QPainterPathStroker::QPainterPathStroker()
2584 : d_ptr(
new QPainterPathStrokerPrivate)
2589
2590
2591
2592
2593QPainterPathStroker::QPainterPathStroker(
const QPen &pen)
2594 : d_ptr(
new QPainterPathStrokerPrivate)
2596 setWidth(pen.widthF());
2597 setCapStyle(pen.capStyle());
2598 setJoinStyle(pen.joinStyle());
2599 setMiterLimit(pen.miterLimit());
2600 setDashOffset(pen.dashOffset());
2602 if (pen.style() == Qt::CustomDashLine)
2603 setDashPattern(pen.dashPattern());
2605 setDashPattern(pen.style());
2609
2610
2611QPainterPathStroker::~QPainterPathStroker()
2617
2618
2619
2620
2621
2622
2623
2624
2625
2626
2627
2628
2629QPainterPath QPainterPathStroker::createStroke(
const QPainterPath &path)
const
2631 QPainterPathStrokerPrivate *d =
const_cast<QPainterPathStrokerPrivate *>(d_func());
2632 QPainterPath stroke;
2635 if (d->dashPattern.isEmpty()) {
2636 d->stroker.strokePath(path, &stroke, QTransform());
2638 QDashStroker dashStroker(&d->stroker);
2639 dashStroker.setDashPattern(d->dashPattern);
2640 dashStroker.setDashOffset(d->dashOffset);
2641 dashStroker.setClipRect(d->stroker.clipRect());
2642 dashStroker.strokePath(path, &stroke, QTransform());
2644 stroke.setFillRule(Qt::WindingFill);
2649
2650
2651
2652
2653
2654void QPainterPathStroker::setWidth(qreal width)
2656 Q_D(QPainterPathStroker);
2659 d->stroker.setStrokeWidth(qt_real_to_fixed(width));
2663
2664
2665qreal QPainterPathStroker::width()
const
2667 return qt_fixed_to_real(d_func()->stroker.strokeWidth());
2672
2673
2674
2675
2676void QPainterPathStroker::setCapStyle(Qt::PenCapStyle style)
2678 d_func()->stroker.setCapStyle(style);
2683
2684
2685Qt::PenCapStyle QPainterPathStroker::capStyle()
const
2687 return d_func()->stroker.capStyle();
2691
2692
2693void QPainterPathStroker::setJoinStyle(Qt::PenJoinStyle style)
2695 d_func()->stroker.setJoinStyle(style);
2699
2700
2701Qt::PenJoinStyle QPainterPathStroker::joinStyle()
const
2703 return d_func()->stroker.joinStyle();
2707
2708
2709
2710
2711
2712
2713
2714
2715
2716void QPainterPathStroker::setMiterLimit(qreal limit)
2718 d_func()->stroker.setMiterLimit(qt_real_to_fixed(limit));
2722
2723
2724qreal QPainterPathStroker::miterLimit()
const
2726 return qt_fixed_to_real(d_func()->stroker.miterLimit());
2731
2732
2733
2734
2735
2736
2737
2738void QPainterPathStroker::setCurveThreshold(qreal threshold)
2740 d_func()->stroker.setCurveThreshold(qt_real_to_fixed(threshold));
2744
2745
2746
2747qreal QPainterPathStroker::curveThreshold()
const
2749 return qt_fixed_to_real(d_func()->stroker.curveThreshold());
2753
2754
2755void QPainterPathStroker::setDashPattern(Qt::PenStyle style)
2757 d_func()->dashPattern = QDashStroker::patternForStyle(style);
2761
2762
2763
2764
2765
2766
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776void QPainterPathStroker::setDashPattern(
const QList<qreal> &dashPattern)
2778 d_func()->dashPattern.clear();
2779 for (
int i=0; i<dashPattern.size(); ++i)
2780 d_func()->dashPattern << qt_real_to_fixed(dashPattern.at(i));
2784
2785
2786QList<qreal> QPainterPathStroker::dashPattern()
const
2788 return d_func()->dashPattern;
2792
2793
2794qreal QPainterPathStroker::dashOffset()
const
2796 return d_func()->dashOffset;
2800
2801
2802
2803
2804
2805void QPainterPathStroker::setDashOffset(qreal offset)
2807 d_func()->dashOffset = offset;
2811
2812
2813
2814
2815
2816
2817
2818
2819
2820
2821
2822
2823
2824
2825QPolygonF QPainterPath::toFillPolygon(
const QTransform &matrix)
const
2827 const QList<QPolygonF> flats = toSubpathPolygons(matrix);
2829 if (flats.isEmpty())
2831 QPointF first = flats.first().first();
2832 for (
int i=0; i<flats.size(); ++i) {
2833 polygon += flats.at(i);
2834 if (!flats.at(i).isClosed())
2835 polygon += flats.at(i).first();
2843
2844
2845
2846
2847
2848bool QPainterPath::isCachingEnabled()
const
2851 return d && d->cacheEnabled;
2855
2856
2857
2858
2859
2860
2861
2862
2863
2864
2865
2866void QPainterPath::setCachingEnabled(
bool enabled)
2869 if (d_func()->cacheEnabled == enabled)
2872 QPainterPathPrivate *d = d_func();
2873 d->cacheEnabled = enabled;
2875 d->m_runLengths.clear();
2876 d->m_runLengths.squeeze();
2883 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
2887
2888
2889qreal QPainterPath::length()
const
2894 if (d->cacheEnabled) {
2895 if (d->dirtyRunLengths)
2896 d->computeRunLengths();
2897 return d->m_runLengths.last();
2901 for (
int i=1; i<d->elements.size(); ++i) {
2902 const Element &e = d->elements.at(i);
2909 len += QLineF(d->elements.at(i-1), e).length();
2912 case CurveToElement:
2914 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
2916 d->elements.at(i+1),
2917 d->elements.at(i+2));
2930
2931
2932
2933
2934
2935
2936
2937qreal QPainterPath::percentAtLength(qreal len)
const
2940 if (isEmpty() || len <= 0)
2943 qreal totalLength = length();
2944 if (len > totalLength)
2947 Q_ASSERT(totalLength != 0);
2949 if (d->cacheEnabled) {
2950 const int ei = qMax(d->elementAtT(len / totalLength), 1);
2952 const QPainterPath::Element &e = d->elements[ei];
2954 case QPainterPath::LineToElement:
2955 res = len / totalLength;
2957 case CurveToElement:
2959 QBezier b = QBezier::fromPoints(d->elements.at(ei-1),
2961 d->elements.at(ei+1),
2962 d->elements.at(ei+2));
2963 qreal prevLen = d->m_runLengths[ei - 1];
2964 qreal blen = d->m_runLengths[ei] - prevLen;
2965 qreal elemRes = b.tAtLength(len - prevLen);
2966 res = (elemRes * blen + prevLen) / totalLength;
2976 for (
int i=1; i<d->elements.size(); ++i) {
2977 const Element &e = d->elements.at(i);
2984 QLineF line(d->elements.at(i-1), e);
2985 qreal llen = line.length();
2987 if (curLen >= len) {
2988 return len/totalLength ;
2993 case CurveToElement:
2995 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
2997 d->elements.at(i+1),
2998 d->elements.at(i+2));
2999 qreal blen = b.length();
3000 qreal prevLen = curLen;
3003 if (curLen >= len) {
3004 qreal res = b.tAtLength(len - prevLen);
3005 return (res * blen + prevLen)/totalLength;
3020 qreal *bezierLength)
3022 *startingLength = 0;
3027 qreal totalLength = path.length();
3029 const int lastElement = path.elementCount() - 1;
3030 for (
int i=0; i <= lastElement; ++i) {
3031 const QPainterPath::Element &e = path.elementAt(i);
3034 case QPainterPath::MoveToElement:
3036 case QPainterPath::LineToElement:
3038 QLineF line(path.elementAt(i-1), e);
3039 qreal llen = line.length();
3041 if (i == lastElement || curLen/totalLength >= t) {
3042 *bezierLength = llen;
3043 QPointF a = path.elementAt(i-1);
3044 QPointF delta = e - a;
3045 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
3049 case QPainterPath::CurveToElement:
3051 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
3053 path.elementAt(i+1),
3054 path.elementAt(i+2));
3055 qreal blen = b.length();
3058 if (i + 2 == lastElement || curLen/totalLength >= t) {
3059 *bezierLength = blen;
3069 *startingLength = curLen;
3075 qreal *bezierLength)
const
3077 Q_ASSERT(t >= 0 && t <= 1);
3079 if (!path.isEmpty() && d->cacheEnabled) {
3080 const int ei = qMax(d->elementAtT(t), 1);
3081 const qreal prevRunLength = d->m_runLengths[ei - 1];
3082 *startingLength = prevRunLength;
3083 *bezierLength = d->m_runLengths[ei] - prevRunLength;
3084 const QPointF prev = d->elements[ei - 1];
3085 const QPainterPath::Element &e = d->elements[ei];
3087 case QPainterPath::LineToElement:
3089 QPointF delta = (e - prev) / 3;
3090 return QBezier::fromPoints(prev, prev + delta, prev + 2 * delta, e);
3092 case QPainterPath::CurveToElement:
3093 return QBezier::fromPoints(prev, e, elements[ei + 1], elements[ei + 2]);
3100 return uncached_bezierAtT(path, t, startingLength, bezierLength);
3104
3105
3106
3107
3108
3109
3110
3111
3112QPointF QPainterPath::pointAtPercent(qreal t)
const
3114 if (t < 0 || t > 1) {
3115 qWarning(
"QPainterPath::pointAtPercent accepts only values between 0 and 1");
3119 if (!d_ptr || d_ptr->elements.size() == 0)
3122 if (d_ptr->elements.size() == 1)
3123 return d_ptr->elements.at(0);
3125 qreal totalLength = length();
3127 qreal bezierLen = 0;
3128 QBezier b = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3129 Q_ASSERT(bezierLen != 0);
3130 qreal realT = (totalLength * t - curLen) / bezierLen;
3132 return b.pointAt(qBound(qreal(0), realT, qreal(1)));
3136
3137
3138
3139
3140
3141
3142
3143
3144
3145
3146
3147qreal QPainterPath::angleAtPercent(qreal t)
const
3149 if (t < 0 || t > 1) {
3150 qWarning(
"QPainterPath::angleAtPercent accepts only values between 0 and 1");
3157 qreal totalLength = length();
3159 qreal bezierLen = 0;
3160 QBezier bez = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3161 Q_ASSERT(bezierLen != 0);
3162 qreal realT = (totalLength * t - curLen) / bezierLen;
3164 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3165 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3167 return QLineF(0, 0, m1, m2).angle();
3172
3173
3174
3175
3176
3177
3178
3179
3180qreal QPainterPath::slopeAtPercent(qreal t)
const
3182 if (t < 0 || t > 1) {
3183 qWarning(
"QPainterPath::slopeAtPercent accepts only values between 0 and 1");
3190 qreal totalLength = length();
3192 qreal bezierLen = 0;
3193 QBezier bez = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3194 Q_ASSERT(bezierLen != 0);
3195 qreal realT = (totalLength * t - curLen) / bezierLen;
3197 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3198 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3205 if (std::numeric_limits<qreal>::has_infinity) {
3206 slope = (m2 < 0) ? -std::numeric_limits<qreal>::infinity()
3207 : std::numeric_limits<qreal>::infinity();
3209 if (
sizeof(qreal) ==
sizeof(
double)) {
3210 return 1.79769313486231570e+308;
3212 return ((qreal)3.40282346638528860e+38);
3221
3222
3223
3224
3225
3226
3227
3228
3229
3230
3231
3232
3233
3234
3235
3237QPainterPath QPainterPath::trimmed(qreal fromFraction, qreal toFraction, qreal offset)
const
3243 if (!isCachingEnabled()) {
3244 QPainterPath copy(*
this);
3245 copy.setCachingEnabled(
true);
3246 return copy.trimmed(fromFraction, toFraction, offset);
3249 qreal f1 = qBound(qreal(0), fromFraction, qreal(1));
3250 qreal f2 = qBound(qreal(0), toFraction, qreal(1));
3251 if (qFuzzyIsNull(f1 - f2))
3252 return QPainterPath();
3255 if (qFuzzyCompare(f2 - f1, qreal(1)))
3259 res.setFillRule(fillRule());
3263 offset = std::modf(offset, &dummy);
3265 qreal of1 = f1 + offset;
3266 qreal of2 = f2 + offset;
3268 f1 = of1 < 0 ? of1 + 1 : of1;
3269 f2 = of2 + 1 > 1 ? of2 : of2 + 1;
3270 }
else if (offset > 0) {
3271 f1 = of1 - 1 < 0 ? of1 : of1 - 1;
3272 f2 = of2 > 1 ? of2 - 1 : of2;
3275 const bool wrapping = (f1 > f2);
3278 QPainterPathPrivate *d = d_func();
3279 if (d->dirtyRunLengths)
3280 d->computeRunLengths();
3281 const qreal totalLength = d->m_runLengths.last();
3282 if (qFuzzyIsNull(totalLength))
3285 const qreal l1 = f1 * totalLength;
3286 const qreal l2 = f2 * totalLength;
3287 const int e1 = d->elementAtLength(l1);
3288 const bool mustTrimE1 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e1), l1);
3289 const int e2 = d->elementAtLength(l2);
3290 const bool mustTrimE2 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e2), l2);
3293 if (e1 == e2 && !wrapping && mustTrimE1 && mustTrimE2) {
3295 d->appendSliceOfElement(&res, e1, l1, l2);
3299 d->appendEndOfElement(&res, e1, l1);
3301 res.moveTo(d->endPointOfElement(e1));
3304 int firstWholeElement = e1 + 1;
3305 int lastWholeElement = (mustTrimE2 ? e2 - 1 : e2);
3307 d->appendElementRange(&res, firstWholeElement, lastWholeElement);
3309 int lastIndex = d->elements.size() - 1;
3310 d->appendElementRange(&res, firstWholeElement, lastIndex);
3311 bool isClosed = (QPointF(d->elements.at(0)) == QPointF(d->elements.at(lastIndex)));
3313 d->appendElementRange(&res, (isClosed ? 1 : 0), lastWholeElement);
3318 d->appendStartOfElement(&res, e2, l2);
3325 qreal startLen, qreal endLen)
3327 Q_ASSERT(cacheEnabled);
3328 Q_ASSERT(!dirtyRunLengths);
3330 if (elemIdx <= 0 || elemIdx >= elements.size())
3333 const qreal prevLen = m_runLengths.at(elemIdx - 1);
3334 const qreal elemLen = m_runLengths.at(elemIdx) - prevLen;
3335 const qreal len1 = startLen - prevLen;
3336 const qreal len2 = endLen - prevLen;
3337 if (qFuzzyIsNull(elemLen))
3340 const QPointF pp = elements.at(elemIdx - 1);
3341 const QPainterPath::Element e = elements.at(elemIdx);
3344 QPointF p1 = (trimFlags & TrimStart) ? l.pointAt(len1 / elemLen) : pp;
3345 QPointF p2 = (trimFlags & TrimEnd) ? l.pointAt(len2 / elemLen) : e;
3349 }
else if (e.isCurveTo()) {
3350 Q_ASSERT(elemIdx < elements.size() - 2);
3351 QBezier b = QBezier::fromPoints(pp, e, elements.at(elemIdx + 1), elements.at(elemIdx + 2));
3352 qreal t1 = (trimFlags & TrimStart) ? b.tAtLength(len1) : 0.0;
3353 qreal t2 = (trimFlags & TrimEnd) ? b.tAtLength(len2) : 1.0;
3354 QBezier c = b.getSubRange(t1, t2);
3356 to->moveTo(c.pt1());
3357 to->cubicTo(c.pt2(), c.pt3(), c.pt4());
3365 if (first < 0 || first >= elements.size() || last < 0 || last >= elements.size())
3369 for (
int i = first; i <= last; i++) {
3370 const QPainterPath::Element &e = elements.at(i);
3372 case QPainterPath::MoveToElement:
3375 case QPainterPath::LineToElement:
3378 case QPainterPath::CurveToElement:
3379 Q_ASSERT(i < elements.size() - 2);
3380 to->cubicTo(e, elements.at(i + 1), elements.at(i + 2));
3392
3393
3394
3395
3396
3397
3398
3399
3400
3401
3402
3403
3404void QPainterPath::addRoundedRect(
const QRectF &rect, qreal xRadius, qreal yRadius,
3407 QRectF r = rect.normalized();
3412 if (mode == Qt::AbsoluteSize) {
3413 qreal w = r.width() / 2;
3414 qreal h = r.height() / 2;
3419 xRadius = 100 * qMin(xRadius, w) / w;
3424 yRadius = 100 * qMin(yRadius, h) / h;
3434 if (xRadius <= 0 || yRadius <= 0) {
3441 qreal w = r.width();
3442 qreal h = r.height();
3443 qreal rxx2 = w*xRadius/100;
3444 qreal ryy2 = h*yRadius/100;
3449 bool first = d_func()->elements.size() < 2;
3451 arcMoveTo(x, y, rxx2, ryy2, 180);
3452 arcTo(x, y, rxx2, ryy2, 180, -90);
3453 arcTo(x+w-rxx2, y, rxx2, ryy2, 90, -90);
3454 arcTo(x+w-rxx2, y+h-ryy2, rxx2, ryy2, 0, -90);
3455 arcTo(x, y+h-ryy2, rxx2, ryy2, 270, -90);
3458 d_func()->require_moveTo =
true;
3459 d_func()->convex = first;
3463
3464
3465
3466
3467
3468
3471
3472
3473
3474
3475
3476
3477
3478
3479
3480
3481
3482QPainterPath QPainterPath::united(
const QPainterPath &p)
const
3484 if (isEmpty() || p.isEmpty())
3485 return isEmpty() ? p : *
this;
3486 QPathClipper clipper(*
this, p);
3487 return clipper.clip(QPathClipper::BoolOr);
3491
3492
3493
3494
3495
3496
3497QPainterPath QPainterPath::intersected(
const QPainterPath &p)
const
3499 if (isEmpty() || p.isEmpty())
3500 return QPainterPath();
3501 QPathClipper clipper(*
this, p);
3502 return clipper.clip(QPathClipper::BoolAnd);
3506
3507
3508
3509
3510
3511
3512
3513
3514
3515QPainterPath QPainterPath::subtracted(
const QPainterPath &p)
const
3517 if (isEmpty() || p.isEmpty())
3519 QPathClipper clipper(*
this, p);
3520 return clipper.clip(QPathClipper::BoolSub);
3524
3525
3526
3527
3528
3529
3530
3531
3532QPainterPath QPainterPath::simplified()
const
3536 QPathClipper clipper(*
this, QPainterPath());
3537 return clipper.clip(QPathClipper::Simplify);
3541
3542
3543
3544
3545
3546
3547
3548
3549
3550
3551bool QPainterPath::intersects(
const QPainterPath &p)
const
3553 if (p.elementCount() == 1)
3554 return contains(p.elementAt(0));
3555 if (isEmpty() || p.isEmpty())
3557 QPathClipper clipper(*
this, p);
3558 return clipper.intersect();
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573bool QPainterPath::contains(
const QPainterPath &p)
const
3575 if (p.elementCount() == 1)
3576 return contains(p.elementAt(0));
3577 if (isEmpty() || p.isEmpty())
3579 QPathClipper clipper(*
this, p);
3580 return clipper.contains();
3583void QPainterPath::setDirty(
bool dirty)
3585 d_func()->pathConverter.reset();
3586 d_func()->dirtyBounds = dirty;
3587 d_func()->dirtyControlBounds = dirty;
3588 d_func()->dirtyRunLengths = dirty;
3589 d_func()->convex =
false;
3592void QPainterPath::computeBoundingRect()
const
3594 QPainterPathPrivate *d = d_func();
3595 d->dirtyBounds =
false;
3597 d->bounds = QRect();
3601 qreal minx, maxx, miny, maxy;
3602 minx = maxx = d->elements.at(0).x;
3603 miny = maxy = d->elements.at(0).y;
3604 for (
int i=1; i<d->elements.size(); ++i) {
3605 const Element &e = d->elements.at(i);
3610 if (e.x > maxx) maxx = e.x;
3611 else if (e.x < minx) minx = e.x;
3612 if (e.y > maxy) maxy = e.y;
3613 else if (e.y < miny) miny = e.y;
3615 case CurveToElement:
3617 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
3619 d->elements.at(i+1),
3620 d->elements.at(i+2));
3621 QRectF r = qt_painterpath_bezier_extrema(b);
3622 qreal right = r.right();
3623 qreal bottom = r.bottom();
3624 if (r.x() < minx) minx = r.x();
3625 if (right > maxx) maxx = right;
3626 if (r.y() < miny) miny = r.y();
3627 if (bottom > maxy) maxy = bottom;
3635 d->bounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3639void QPainterPath::computeControlPointRect()
const
3641 QPainterPathPrivate *d = d_func();
3642 d->dirtyControlBounds =
false;
3644 d->controlBounds = QRect();
3648 qreal minx, maxx, miny, maxy;
3649 minx = maxx = d->elements.at(0).x;
3650 miny = maxy = d->elements.at(0).y;
3651 for (
int i=1; i<d->elements.size(); ++i) {
3652 const Element &e = d->elements.at(i);
3653 if (e.x > maxx) maxx = e.x;
3654 else if (e.x < minx) minx = e.x;
3655 if (e.y > maxy) maxy = e.y;
3656 else if (e.y < miny) miny = e.y;
3658 d->controlBounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3663 Q_ASSERT(!elements.isEmpty());
3665 m_runLengths.clear();
3666 const int numElems = elements.size();
3667 m_runLengths.reserve(numElems);
3669 QPointF runPt = elements[0];
3671 for (
int i = 0; i < numElems; i++) {
3672 QPainterPath::Element e = elements[i];
3674 case QPainterPath::LineToElement:
3675 runLen += QLineF(runPt, e).length();
3678 case QPainterPath::CurveToElement: {
3679 Q_ASSERT(i < numElems - 2);
3680 QPainterPath::Element ee = elements[i + 2];
3681 runLen += QBezier::fromPoints(runPt, e, elements[i + 1], ee).length();
3685 case QPainterPath::MoveToElement:
3688 case QPainterPath::CurveToDataElement:
3691 m_runLengths.append(runLen);
3693 Q_ASSERT(m_runLengths.size() == elements.size());
3695 dirtyRunLengths =
false;
3698#ifndef QT_NO_DEBUG_STREAM
3701 QDebugStateSaver saver(s);
3702 s.nospace() <<
"QPainterPath: Element count=" << p.elementCount() << Qt::endl;
3703 const char *types[] = {
"MoveTo",
"LineTo",
"CurveTo",
"CurveToData"};
3704 for (
int i=0; i<p.elementCount(); ++i) {
3705 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)