14#include <qtextlayout.h>
15#include <qvarlengtharray.h>
18#include <private/qbezier_p.h>
19#include <private/qfontengine_p.h>
20#include <private/qnumeric_p.h>
21#include <private/qobject_p.h>
22#include <private/qpathclipper_p.h>
23#include <private/qstroker_p.h>
24#include <private/qtextengine_p.h>
31#include <performance.h>
42 if (
sizeof(qreal) >=
sizeof(
double))
43 return qIsFinite(c) && fabs(c) < 1e128;
45 return qIsFinite(c) && fabsf(
float(c)) < 1e16f;
50 return isValidCoord(p.x()) && isValidCoord(p.y());
55 return isValidCoord(r.x()) && isValidCoord(r.y()) && isValidCoord(r.width()) && isValidCoord(r.height());
69 QPointF* startPoint, QPointF *endPoint)
73 *startPoint = QPointF();
75 *endPoint = QPointF();
79 qreal w2 = r.width() / 2;
80 qreal h2 = r.height() / 2;
82 qreal angles[2] = { angle, angle + length };
83 QPointF *points[2] = { startPoint, endPoint };
85 for (
int i = 0; i < 2; ++i) {
89 qreal theta = angles[i] - 360 *
std::floor(angles[i] / 360);
92 int quadrant =
int(t);
95 t = qt_t_for_arc_angle(90 * t);
102 QBezier::coefficients(t, a, b, c, d);
103 QPointF p(a + b + c*QT_PATH_KAPPA, d + c + b*QT_PATH_KAPPA);
106 if (quadrant == 1 || quadrant == 2)
110 if (quadrant == 0 || quadrant == 1)
113 *points[i] = r.center() + QPointF(w2 * p.x(), h2 * p.y());
118static void qt_debug_path(
const QPainterPath &path)
120 const char *names[] = {
127 printf(
"\nQPainterPath: elementCount=%d\n", path.elementCount());
128 for (
int i=0; i<path.elementCount(); ++i) {
129 const QPainterPath::Element &e = path.elementAt(i);
130 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement);
131 printf(
" - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y);
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
345
346
347
348
349
352
353
354
355
356
359
360
361
362
363
366
367
368
369
370
371
372
373
376
377
378
379
380
381
382
383
386
387
388
389
390
391
394
395
396
397
398
399
402
403
404
405
406
407
408
411
412
413
414
415
416
419
420
421
422
423
424
425
428
429
430
431
432
433
434
435
438
439
440
441
442
443
444
447
448
449
450
451
452
454int QPainterPath::elementCount()
const
456 return d_ptr ? d_ptr->elements.size() : 0;
460
461
462
463
464
465
467QPainterPath::Element QPainterPath::elementAt(
int i)
const
470 Q_ASSERT(i >= 0 && i < elementCount());
471 return d_ptr->elements.at(i);
475
476
477
478
479
480
482void QPainterPath::setElementPositionAt(
int i, qreal x, qreal y)
485 Q_ASSERT(i >= 0 && i < elementCount());
487 QPainterPath::Element &e = d_ptr->elements[i];
494
495
496
497
498
501
502
503QPainterPath::QPainterPath()
noexcept
509
510
511
512
513
514
515QPainterPath::QPainterPath(
const QPainterPath &other)
516 : d_ptr(other.d_ptr ?
new QPainterPathPrivate(*other.d_ptr) :
nullptr)
521
522
523
524
525
526
527
530
531
532
534QPainterPath::QPainterPath(
const QPointF &startPoint)
535 : d_ptr(
new QPainterPathPrivate(startPoint))
540
541
542void QPainterPath::ensureData_helper()
544 Q_ASSERT(d_ptr ==
nullptr);
545 QPainterPathPrivate *data =
new QPainterPathPrivate;
546 data->elements.reserve(16);
547 QPainterPath::Element e = { 0, 0, QPainterPath::MoveToElement };
550 Q_ASSERT(d_ptr !=
nullptr);
554
555
556
557
558
559
560QPainterPath &QPainterPath::operator=(
const QPainterPath &other)
562 QPainterPath copy(other);
568
569
570
571
572
573
576
577
578
579
582
583
584QPainterPath::~QPainterPath()
590
591
592
593
594
595
596
597void QPainterPath::clear()
604 d_func()->elements.append( {0, 0, MoveToElement} );
608
609
610
611
612
613
614
615void QPainterPath::reserve(
int size)
618 if ((!d && size > 0) || (d && d->elements.capacity() < size)) {
621 d_func()->elements.reserve(size);
626
627
628
629
630
631int QPainterPath::capacity()
const
635 return d->elements.capacity();
641
642
643
644
645
646
647
648
649
650
651void QPainterPath::closeSubpath()
654 printf(
"QPainterPath::closeSubpath()\n");
664
665
666
667
668
669
670
673
674
675
676
677
678
679
680
681void QPainterPath::moveTo(
const QPointF &p)
684 printf(
"QPainterPath::moveTo() (%.2f,%.2f)\n", p.x(), p.y());
687 if (!hasValidCoords(p)) {
689 qWarning(
"QPainterPath::moveTo: Adding point with invalid coordinates, ignoring call");
697 QPainterPathPrivate *d = d_func();
698 Q_ASSERT(!d->elements.isEmpty());
700 d->require_moveTo =
false;
702 if (d->elements.constLast().type == MoveToElement) {
703 d->elements.last().x = p.x();
704 d->elements.last().y = p.y();
706 Element elm = { p.x(), p.y(), MoveToElement };
707 d->elements.append(elm);
709 d->cStart = d->elements.size() - 1;
713
714
715
716
717
718
719
722
723
724
725
726
727
728
729
730
731void QPainterPath::lineTo(
const QPointF &p)
734 printf(
"QPainterPath::lineTo() (%.2f,%.2f)\n", p.x(), p.y());
737 if (!hasValidCoords(p)) {
739 qWarning(
"QPainterPath::lineTo: Adding point with invalid coordinates, ignoring call");
747 QPainterPathPrivate *d = d_func();
748 Q_ASSERT(!d->elements.isEmpty());
750 if (p == QPointF(d->elements.constLast()))
752 Element elm = { p.x(), p.y(), LineToElement };
753 d->elements.append(elm);
755 d->convex = d->elements.size() == 3 || (d->elements.size() == 4 && d->isClosed());
759
760
761
762
763
764
765
766
767
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790void QPainterPath::cubicTo(
const QPointF &c1,
const QPointF &c2,
const QPointF &e)
793 printf(
"QPainterPath::cubicTo() (%.2f,%.2f), (%.2f,%.2f), (%.2f,%.2f)\n",
794 c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y());
797 if (!hasValidCoords(c1) || !hasValidCoords(c2) || !hasValidCoords(e)) {
799 qWarning(
"QPainterPath::cubicTo: Adding point with invalid coordinates, ignoring call");
807 QPainterPathPrivate *d = d_func();
808 Q_ASSERT(!d->elements.isEmpty());
813 if (d->elements.constLast() == c1 && c1 == c2 && c2 == e)
818 Element ce1 = { c1.x(), c1.y(), CurveToElement };
819 Element ce2 = { c2.x(), c2.y(), CurveToDataElement };
820 Element ee = { e.x(), e.y(), CurveToDataElement };
821 d->elements << ce1 << ce2 << ee;
825
826
827
828
829
830
831
832
835
836
837
838
839
840
841
842
843
844
845
846void QPainterPath::quadTo(
const QPointF &c,
const QPointF &e)
849 printf(
"QPainterPath::quadTo() (%.2f,%.2f), (%.2f,%.2f)\n",
850 c.x(), c.y(), e.x(), e.y());
853 if (!hasValidCoords(c) || !hasValidCoords(e)) {
855 qWarning(
"QPainterPath::quadTo: Adding point with invalid coordinates, ignoring call");
864 Q_ASSERT(!d->elements.isEmpty());
865 const QPainterPath::Element &elm = d->elements.at(elementCount()-1);
866 QPointF prev(elm.x, elm.y);
870 if (prev == c && c == e)
873 QPointF c1((prev.x() + 2*c.x()) / 3, (prev.y() + 2*c.y()) / 3);
874 QPointF c2((e.x() + 2*c.x()) / 3, (e.y() + 2*c.y()) / 3);
879
880
881
882
883
884
885
886
887
888
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918void QPainterPath::arcTo(
const QRectF &rect, qreal startAngle, qreal sweepLength)
921 printf(
"QPainterPath::arcTo() (%.2f, %.2f, %.2f, %.2f, angle=%.2f, sweep=%.2f\n",
922 rect.x(), rect.y(), rect.width(), rect.height(), startAngle, sweepLength);
925 if (!hasValidCoords(rect) || !isValidCoord(startAngle) || !isValidCoord(sweepLength)) {
927 qWarning(
"QPainterPath::arcTo: Adding point with invalid coordinates, ignoring call");
940 QPointF curve_start = qt_curves_for_arc(rect, startAngle, sweepLength, pts, &point_count);
943 for (
int i=0; i<point_count; i+=3) {
944 cubicTo(pts[i].x(), pts[i].y(),
945 pts[i+1].x(), pts[i+1].y(),
946 pts[i+2].x(), pts[i+2].y());
953
954
955
956
957
958
959
963
964
965
966
967
968
969
970
971
972
973
975void QPainterPath::arcMoveTo(
const QRectF &rect, qreal angle)
977 if (!hasValidCoords(rect) || !isValidCoord(angle)) {
979 qWarning(
"QPainterPath::arcMoveTo: Adding point with invalid coordinates, ignoring call");
988 qt_find_ellipse_coords(rect, angle, 0, &pt,
nullptr);
995
996
997
998
999QPointF QPainterPath::currentPosition()
const
1001 return !d_ptr || d_func()->elements.isEmpty()
1003 : QPointF(d_func()->elements.constLast().x, d_func()->elements.constLast().y);
1008
1009
1010
1011
1012
1013
1014
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036void QPainterPath::addRect(
const QRectF &r)
1038 if (!hasValidCoords(r)) {
1040 qWarning(
"QPainterPath::addRect: Adding point with invalid coordinates, ignoring call");
1051 bool first = d_func()->elements.size() < 2;
1053 moveTo(r.x(), r.y());
1055 Element l1 = { r.x() + r.width(), r.y(), LineToElement };
1056 Element l2 = { r.x() + r.width(), r.y() + r.height(), LineToElement };
1057 Element l3 = { r.x(), r.y() + r.height(), LineToElement };
1058 Element l4 = { r.x(), r.y(), LineToElement };
1060 d_func()->elements << l1 << l2 << l3 << l4;
1061 d_func()->require_moveTo =
true;
1062 d_func()->convex = first;
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083void QPainterPath::addPolygon(
const QPolygonF &polygon)
1085 if (polygon.isEmpty())
1091 moveTo(polygon.constFirst());
1092 for (
int i=1; i<polygon.size(); ++i) {
1093 Element elm = { polygon.at(i).x(), polygon.at(i).y(), LineToElement };
1094 d_func()->elements << elm;
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118void QPainterPath::addEllipse(
const QRectF &boundingRect)
1120 if (!hasValidCoords(boundingRect)) {
1122 qWarning(
"QPainterPath::addEllipse: Adding point with invalid coordinates, ignoring call");
1127 if (boundingRect.isNull())
1133 bool first = d_func()->elements.size() < 2;
1137 QPointF start = qt_curves_for_arc(boundingRect, 0, -360, pts, &point_count);
1140 cubicTo(pts[0], pts[1], pts[2]);
1141 cubicTo(pts[3], pts[4], pts[5]);
1142 cubicTo(pts[6], pts[7], pts[8]);
1143 cubicTo(pts[9], pts[10], pts[11]);
1144 d_func()->require_moveTo =
true;
1146 d_func()->convex = first;
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170void QPainterPath::addText(
const QPointF &point,
const QFont &f,
const QString &text)
1178 QTextLayout layout(text, f);
1179 layout.setCacheEnabled(
true);
1181 QTextOption opt = layout.textOption();
1182 opt.setUseDesignMetrics(
true);
1183 layout.setTextOption(opt);
1185 QTextEngine *eng = layout.engine();
1186 layout.beginLayout();
1187 QTextLine line = layout.createLine();
1190 const QScriptLine &sl = eng->lines[0];
1191 if (!sl.length || !eng->layoutData)
1194 int nItems = eng->layoutData->items.size();
1199 QVarLengthArray<
int> visualOrder(nItems);
1200 QVarLengthArray<uchar> levels(nItems);
1201 for (
int i = 0; i < nItems; ++i)
1202 levels[i] = eng->layoutData->items.at(i).analysis.bidiLevel;
1203 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data());
1205 for (
int i = 0; i < nItems; ++i) {
1206 int item = visualOrder[i];
1207 const QScriptItem &si = eng->layoutData->items.at(item);
1209 if (si.analysis.flags < QScriptAnalysis::TabOrObject) {
1210 QGlyphLayout glyphs = eng->shapedGlyphs(&si);
1211 QFontEngine *fe = eng->fontEngine(si);
1213 fe->addOutlineToPath(x, y, glyphs,
this,
1214 si.analysis.bidiLevel % 2
1215 ? QTextItem::RenderFlags(QTextItem::RightToLeft)
1216 : QTextItem::RenderFlags{});
1218 const qreal lw = fe->lineThickness().toReal();
1219 if (f.d->underline) {
1220 qreal pos = fe->underlinePosition().toReal();
1221 addRect(x, y + pos, si.width.toReal(), lw);
1223 if (f.d->overline) {
1224 qreal pos = fe->ascent().toReal() + 1;
1225 addRect(x, y - pos, si.width.toReal(), lw);
1227 if (f.d->strikeOut) {
1228 qreal pos = fe->ascent().toReal() / 3;
1229 addRect(x, y - pos, si.width.toReal(), lw);
1232 x += si.width.toReal();
1237
1238
1239
1240
1241
1242
1243
1244void QPainterPath::addPath(
const QPainterPath &other)
1246 if (other.isEmpty())
1252 QPainterPathPrivate *d = d_func();
1254 if (d->elements.constLast().type == MoveToElement)
1255 d->elements.remove(d->elements.size()-1);
1258 int cStart = d->elements.size() + other.d_func()->cStart;
1259 d->elements += other.d_func()->elements;
1262 d->require_moveTo = other.d_func()->isClosed();
1267
1268
1269
1270
1271
1272
1273
1274
1275void QPainterPath::connectPath(
const QPainterPath &other)
1277 if (other.isEmpty())
1283 QPainterPathPrivate *d = d_func();
1285 if (d->elements.constLast().type == MoveToElement)
1286 d->elements.remove(d->elements.size()-1);
1289 int cStart = d->elements.size() + other.d_func()->cStart;
1290 int first = d->elements.size();
1291 d->elements += other.d_func()->elements;
1294 d->elements[first].type = LineToElement;
1297 if (first > 0 && QPointF(d->elements.at(first)) == QPointF(d->elements.at(first - 1))) {
1298 d->elements.remove(first--);
1302 if (cStart != first)
1307
1308
1309
1310
1311
1312
1313void QPainterPath::addRegion(
const QRegion ®ion)
1318 for (
const QRect &rect : region)
1324
1325
1326
1327
1328Qt::FillRule QPainterPath::fillRule()
const
1330 return d_func() && d_func()->hasWindingFill ? Qt::WindingFill : Qt::OddEvenFill;
1334
1335
1336
1337
1338
1339
1340
1341
1342
1343
1344
1345
1346
1347
1348
1349
1350void QPainterPath::setFillRule(Qt::FillRule fillRule)
1353 const bool isWindingRequested = (fillRule == Qt::WindingFill);
1354 if (d_func()->hasWindingFill == isWindingRequested)
1358 d_func()->hasWindingFill = isWindingRequested;
1361#define QT_BEZIER_A(bezier, coord) 3
* (-bezier.coord##1
1366#define QT_BEZIER_B(bezier, coord) 6
* (bezier.coord##1
1370#define QT_BEZIER_C(bezier, coord) 3
* (- bezier.coord##1
1373#define QT_BEZIER_CHECK_T(bezier, t)
1374 if (t >= 0
&& t <= 1
) {
1375 QPointF p(b.pointAt(t));
1376 if (p.x() < minx) minx = p.x();
1377 else if (p.x() > maxx) maxx = p.x();
1378 if (p.y() < miny) miny = p.y();
1379 else if (p.y() > maxy) maxy = p.y();
1385 qreal minx, miny, maxx, maxy;
1409 if (qFuzzyIsNull(ax)) {
1412 if (!qFuzzyIsNull(bx)) {
1418 const qreal tx = bx * bx - 4 * ax * cx;
1421 qreal temp = qSqrt(tx);
1422 qreal rcp = 1 / (2 * ax);
1423 qreal t1 = (-bx + temp) * rcp;
1426 qreal t2 = (-bx - temp) * rcp;
1439 if (qFuzzyIsNull(ay)) {
1442 if (!qFuzzyIsNull(by)) {
1448 const qreal ty = by * by - 4 * ay * cy;
1451 qreal temp = qSqrt(ty);
1452 qreal rcp = 1 / (2 * ay);
1453 qreal t1 = (-by + temp) * rcp;
1456 qreal t2 = (-by - temp) * rcp;
1461 return QRectF(minx, miny, maxx - minx, maxy - miny);
1465
1466
1467
1468
1469
1470QRectF QPainterPath::boundingRect()
const
1474 QPainterPathPrivate *d = d_func();
1477 computeBoundingRect();
1482
1483
1484
1485
1486
1487
1488
1489
1490
1491QRectF QPainterPath::controlPointRect()
const
1495 QPainterPathPrivate *d = d_func();
1497 if (d->dirtyControlBounds)
1498 computeControlPointRect();
1499 return d->controlBounds;
1504
1505
1506
1507
1508
1509
1510
1512bool QPainterPath::isEmpty()
const
1514 return !d_ptr || (d_ptr->elements.size() == 1 && d_ptr->elements.constFirst().type == MoveToElement);
1518
1519
1520
1521
1522
1523
1524
1525QPainterPath QPainterPath::toReversed()
const
1527 Q_D(
const QPainterPath);
1535 rev.moveTo(d->elements.at(d->elements.size()-1).x, d->elements.at(d->elements.size()-1).y);
1537 for (
int i=d->elements.size()-1; i>=1; --i) {
1538 const QPainterPath::Element &elm = d->elements.at(i);
1539 const QPainterPath::Element &prev = d->elements.at(i-1);
1542 rev.lineTo(prev.x, prev.y);
1545 rev.moveTo(prev.x, prev.y);
1547 case CurveToDataElement:
1550 const QPainterPath::Element &cp1 = d->elements.at(i-2);
1551 const QPainterPath::Element &sp = d->elements.at(i-3);
1552 Q_ASSERT(prev.type == CurveToDataElement);
1553 Q_ASSERT(cp1.type == CurveToElement);
1554 rev.cubicTo(prev.x, prev.y, cp1.x, cp1.y, sp.x, sp.y);
1559 Q_ASSERT(!
"qt_reversed_path");
1568
1569
1570
1571
1572
1573
1574
1575
1576
1577
1578
1579QList<QPolygonF> QPainterPath::toSubpathPolygons(
const QTransform &matrix)
const
1582 Q_D(
const QPainterPath);
1583 QList<QPolygonF> flatCurves;
1588 for (
int i=0; i<elementCount(); ++i) {
1589 const QPainterPath::Element &e = d->elements.at(i);
1591 case QPainterPath::MoveToElement:
1592 if (current.size() > 1)
1593 flatCurves += current;
1595 current.reserve(16);
1596 current += QPointF(e.x, e.y) * matrix;
1598 case QPainterPath::LineToElement:
1599 current += QPointF(e.x, e.y) * matrix;
1601 case QPainterPath::CurveToElement: {
1602 Q_ASSERT(d->elements.at(i+1).type == QPainterPath::CurveToDataElement);
1603 Q_ASSERT(d->elements.at(i+2).type == QPainterPath::CurveToDataElement);
1604 QBezier bezier = QBezier::fromPoints(QPointF(d->elements.at(i-1).x, d->elements.at(i-1).y) * matrix,
1605 QPointF(e.x, e.y) * matrix,
1606 QPointF(d->elements.at(i+1).x, d->elements.at(i+1).y) * matrix,
1607 QPointF(d->elements.at(i+2).x, d->elements.at(i+2).y) * matrix);
1608 bezier.addToPolygon(¤t);
1612 case QPainterPath::CurveToDataElement:
1613 Q_ASSERT(!
"QPainterPath::toSubpathPolygons(), bad element type");
1618 if (current.size()>1)
1619 flatCurves += current;
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646QList<QPolygonF> QPainterPath::toFillPolygons(
const QTransform &matrix)
const
1649 QList<QPolygonF> polys;
1651 QList<QPolygonF> subpaths = toSubpathPolygons(matrix);
1652 int count = subpaths.size();
1657 QList<QRectF> bounds;
1658 bounds.reserve(count);
1659 for (
int i=0; i<count; ++i)
1660 bounds += subpaths.at(i).boundingRect();
1662#ifdef QPP_FILLPOLYGONS_DEBUG
1663 printf(
"QPainterPath::toFillPolygons, subpathCount=%d\n", count);
1664 for (
int i=0; i<bounds.size(); ++i)
1665 qDebug() <<
" bounds" << i << bounds.at(i);
1668 QList< QList<
int> > isects;
1669 isects.resize(count);
1672 for (
int j=0; j<count; ++j) {
1673 if (subpaths.at(j).size() <= 2)
1675 QRectF cbounds = bounds.at(j);
1676 for (
int i=0; i<count; ++i) {
1677 if (cbounds.intersects(bounds.at(i))) {
1683#ifdef QPP_FILLPOLYGONS_DEBUG
1684 printf(
"Intersections before flattening:\n");
1685 for (
int i = 0; i < count; ++i) {
1687 for (
int j = 0; j < isects[i].size(); ++j) {
1688 printf(
"%d ", isects[i][j]);
1695 for (
int i=0; i<count; ++i) {
1696 const QList<
int> ¤t_isects = isects.at(i);
1697 for (
int j=0; j<current_isects.size(); ++j) {
1698 int isect_j = current_isects.at(j);
1701 const QList<
int> &isects_j = isects.at(isect_j);
1702 for (
int k = 0, size = isects_j.size(); k < size; ++k) {
1703 int isect_k = isects_j.at(k);
1704 if (isect_k != i && !isects.at(i).contains(isect_k)) {
1705 isects[i] += isect_k;
1708 isects[isect_j].clear();
1712#ifdef QPP_FILLPOLYGONS_DEBUG
1713 printf(
"Intersections after flattening:\n");
1714 for (
int i = 0; i < count; ++i) {
1716 for (
int j = 0; j < isects[i].size(); ++j) {
1717 printf(
"%d ", isects[i][j]);
1724 for (
int i=0; i<count; ++i) {
1725 const QList<
int> &subpath_list = isects.at(i);
1726 if (!subpath_list.isEmpty()) {
1728 for (
int j=0; j<subpath_list.size(); ++j) {
1729 const QPolygonF &subpath = subpaths.at(subpath_list.at(j));
1731 if (!subpath.isClosed())
1732 buildUp += subpath.first();
1733 if (!buildUp.isClosed())
1734 buildUp += buildUp.constFirst();
1757 if (QtPrivate::fuzzyCompare(y1, y2)) {
1760 }
else if (y2 < y1) {
1761 qreal x_tmp = x2; x2 = x1; x1 = x_tmp;
1762 qreal y_tmp = y2; y2 = y1; y1 = y_tmp;
1766 if (y >= y1 && y < y2) {
1767 qreal x = x1 + ((x2 - x1) / (y2 - y1)) * (y - y1);
1777 int *winding,
int depth = 0)
1781 QRectF bounds = bezier.bounds();
1787 if (y >= bounds.y() && y < bounds.y() + bounds.height()) {
1791 const qreal lower_bound = qreal(.001);
1792 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound)) {
1796 if (bezier.pt1().x() <= x) {
1797 (*winding) += (bezier.pt4().y() > bezier.pt1().y() ? 1 : -1);
1803 const auto halves = bezier.split();
1804 qt_painterpath_isect_curve(halves.first, pt, winding, depth + 1);
1805 qt_painterpath_isect_curve(halves.second, pt, winding, depth + 1);
1810
1811
1812
1813
1814
1815
1816
1817bool QPainterPath::contains(
const QPointF &pt)
const
1819 if (isEmpty() || !controlPointRect().contains(pt))
1822 QPainterPathPrivate *d = d_func();
1824 int winding_number = 0;
1828 for (
int i=0; i<d->elements.size(); ++i) {
1829 const Element &e = d->elements.at(i);
1835 qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number);
1836 last_start = last_pt = e;
1840 qt_painterpath_isect_line(last_pt, e, pt, &winding_number);
1844 case CurveToElement:
1846 const QPainterPath::Element &cp2 = d->elements.at(++i);
1847 const QPainterPath::Element &ep = d->elements.at(++i);
1848 qt_painterpath_isect_curve(QBezier::fromPoints(last_pt, e, cp2, ep),
1849 pt, &winding_number);
1861 if (last_pt != last_start)
1862 qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number);
1864 return (d->hasWindingFill
1865 ? (winding_number != 0)
1866 : ((winding_number % 2) != 0));
1874 qreal left = rect.left();
1875 qreal right = rect.right();
1876 qreal top = rect.top();
1877 qreal bottom = rect.bottom();
1880 int p1 = ((x1 < left) <<
Left)
1881 | ((x1 > right) <<
Right)
1882 | ((y1 < top) <<
Top)
1883 | ((y1 > bottom) <<
Bottom);
1884 int p2 = ((x2 < left) <<
Left)
1885 | ((x2 > right) <<
Right)
1886 | ((y2 < top) <<
Top)
1887 | ((y2 > bottom) <<
Bottom);
1899 y1 += dy/dx * (left - x1);
1901 }
else if (x1 > right) {
1902 y1 -= dy/dx * (x1 - right);
1906 y2 += dy/dx * (left - x2);
1908 }
else if (x2 > right) {
1909 y2 -= dy/dx * (x2 - right);
1913 p1 = ((y1 < top) <<
Top)
1914 | ((y1 > bottom) <<
Bottom);
1915 p2 = ((y2 < top) <<
Top)
1916 | ((y2 > bottom) <<
Bottom);
1923 x1 += dx/dy * (top - y1);
1925 }
else if (y1 > bottom) {
1926 x1 -= dx/dy * (y1 - bottom);
1930 x2 += dx/dy * (top - y2);
1932 }
else if (y2 > bottom) {
1933 x2 -= dx/dy * (y2 - bottom);
1937 p1 = ((x1 < left) <<
Left)
1938 | ((x1 > right) <<
Right);
1939 p2 = ((x2 < left) <<
Left)
1940 | ((x2 > right) <<
Right);
1952 QRectF bounds = bezier.bounds();
1954 if (y >= bounds.top() && y < bounds.bottom()
1955 && bounds.right() >= x1 && bounds.left() < x2) {
1956 const qreal lower_bound = qreal(.01);
1957 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound))
1960 const auto halves = bezier.split();
1961 if (qt_isect_curve_horizontal(halves.first, y, x1, x2, depth + 1)
1962 || qt_isect_curve_horizontal(halves.second, y, x1, x2, depth + 1))
1970 QRectF bounds = bezier.bounds();
1972 if (x >= bounds.left() && x < bounds.right()
1973 && bounds.bottom() >= y1 && bounds.top() < y2) {
1974 const qreal lower_bound = qreal(.01);
1975 if (depth == 32 || (bounds.width() < lower_bound && bounds.height() < lower_bound))
1978 const auto halves = bezier.split();
1979 if (qt_isect_curve_vertical(halves.first, x, y1, y2, depth + 1)
1980 || qt_isect_curve_vertical(halves.second, x, y1, y2, depth + 1))
1988 if ((point.x() == rect.left() || point.x() == rect.right()) &&
1989 (point.y() >= rect.top() && point.y() <= rect.bottom()))
1991 if ((point.y() == rect.top() || point.y() == rect.bottom()) &&
1992 (point.x() >= rect.left() && point.x() <= rect.right()))
1998
1999
2004 enum { OnRect, InsideRect, OutsideRect} edgeStatus = OnRect;
2005 for (
int i=0; i<path->elementCount(); ++i) {
2006 const QPainterPath::Element &e = path->elementAt(i);
2010 case QPainterPath::MoveToElement:
2012 && qFuzzyCompare(last_pt, last_start)
2013 && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
2014 last_start.x(), last_start.y(), rect))
2016 last_start = last_pt = e;
2019 case QPainterPath::LineToElement:
2020 if (qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(), e.x, e.y, rect))
2025 case QPainterPath::CurveToElement:
2027 QPointF cp2 = path->elementAt(++i);
2028 QPointF ep = path->elementAt(++i);
2029 QBezier bezier = QBezier::fromPoints(last_pt, e, cp2, ep);
2030 if (qt_isect_curve_horizontal(bezier, rect.top(), rect.left(), rect.right())
2031 || qt_isect_curve_horizontal(bezier, rect.bottom(), rect.left(), rect.right())
2032 || qt_isect_curve_vertical(bezier, rect.left(), rect.top(), rect.bottom())
2033 || qt_isect_curve_vertical(bezier, rect.right(), rect.top(), rect.bottom()))
2044 if (!pointOnEdge(rect, last_pt)) {
2045 bool contained = rect.contains(last_pt);
2046 switch (edgeStatus) {
2056 edgeStatus = contained ? InsideRect : OutsideRect;
2060 if (last_pt == last_start)
2061 edgeStatus = OnRect;
2066 if (last_pt != last_start
2067 && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(),
2068 last_start.x(), last_start.y(), rect))
2075
2076
2077
2078
2079
2080
2081
2082
2083
2084
2085
2086
2087
2088bool QPainterPath::intersects(
const QRectF &rect)
const
2090 if (elementCount() == 1 && rect.contains(elementAt(0)))
2096 QRectF cp = controlPointRect();
2097 QRectF rn = rect.normalized();
2102 if (qMax(rn.left(), cp.left()) > qMin(rn.right(), cp.right())
2103 || qMax(rn.top(), cp.top()) > qMin(rn.bottom(), cp.bottom()))
2107 if (qt_painterpath_check_crossing(
this, rect))
2110 if (contains(rect.center()))
2116 for (
int i=0; i<d->elements.size(); ++i) {
2117 const Element &e = d->elements.at(i);
2118 if (e.type == QPainterPath::MoveToElement && rect.contains(e))
2126
2127
2128
2129
2130
2131void QPainterPath::translate(qreal dx, qreal dy)
2133 if (!d_ptr || (dx == 0 && dy == 0))
2136 int elementsLeft = d_ptr->elements.size();
2137 if (elementsLeft <= 0)
2141 QPainterPath::Element *element = d_func()->elements.data();
2143 while (elementsLeft--) {
2151
2152
2153
2154
2155
2156
2157
2158
2161
2162
2163
2164
2165
2166QPainterPath QPainterPath::translated(qreal dx, qreal dy)
const
2168 QPainterPath copy(*
this);
2169 copy.translate(dx, dy);
2174
2175
2176
2177
2178
2179
2180
2181
2184
2185
2186
2187
2188
2189bool QPainterPath::contains(
const QRectF &rect)
const
2195 if (isEmpty() || !controlPointRect().contains(rect))
2201 if (qt_painterpath_check_crossing(
this, rect)) {
2202 if (fillRule() == Qt::OddEvenFill) {
2207 if (!contains(rect.topLeft()) ||
2208 !contains(rect.topRight()) ||
2209 !contains(rect.bottomRight()) ||
2210 !contains(rect.bottomLeft()))
2219 if (!contains(rect.center()))
2228 for (
int i=0; i<d->elements.size(); ++i) {
2229 const Element &e = d->elements.at(i);
2230 if (e.type == QPainterPath::MoveToElement && rect.contains(e)) {
2231 if (fillRule() == Qt::OddEvenFill)
2235 for (; !stop && i<d->elements.size(); ++i) {
2236 const Element &el = d->elements.at(i);
2245 case CurveToElement:
2246 if (!contains(d->elements.at(i+2)))
2263static inline bool epsilonCompare(
const QPointF &a,
const QPointF &b,
const QSizeF &epsilon)
2265 return qAbs(a.x() - b.x()) <= epsilon.width()
2266 && qAbs(a.y() - b.y()) <= epsilon.height();
2270
2271
2272
2273
2274
2275
2276
2278bool QPainterPath::operator==(
const QPainterPath &path)
const
2280 QPainterPathPrivate *d = d_func();
2281 QPainterPathPrivate *other_d = path.d_func();
2284 }
else if (!d || !other_d) {
2285 if (!other_d && isEmpty() && elementAt(0) == QPointF() && !d->hasWindingFill)
2287 if (!d && path.isEmpty() && path.elementAt(0) == QPointF() && !other_d->hasWindingFill)
2291 else if (d->hasWindingFill != other_d->hasWindingFill)
2293 else if (d->elements.size() != other_d->elements.size())
2296 const qreal qt_epsilon =
sizeof(qreal) ==
sizeof(
double) ? 1e-12 : qreal(1e-5);
2298 QSizeF epsilon = boundingRect().size();
2299 epsilon.rwidth() *= qt_epsilon;
2300 epsilon.rheight() *= qt_epsilon;
2302 for (
int i = 0; i < d->elements.size(); ++i)
2303 if (d->elements.at(i).type != other_d->elements.at(i).type
2304 || !epsilonCompare(d->elements.at(i), other_d->elements.at(i), epsilon))
2311
2312
2313
2314
2315
2316
2317
2319bool QPainterPath::operator!=(
const QPainterPath &path)
const
2321 return !(*
this==path);
2325
2326
2327
2328
2329
2330
2331QPainterPath QPainterPath::operator&(
const QPainterPath &other)
const
2333 return intersected(other);
2337
2338
2339
2340
2341
2342
2343QPainterPath QPainterPath::operator|(
const QPainterPath &other)
const
2345 return united(other);
2349
2350
2351
2352
2353
2354
2355
2356QPainterPath QPainterPath::operator+(
const QPainterPath &other)
const
2358 return united(other);
2362
2363
2364
2365
2366
2367
2368QPainterPath QPainterPath::operator-(
const QPainterPath &other)
const
2370 return subtracted(other);
2374
2375
2376
2377
2378
2379
2380QPainterPath &QPainterPath::operator&=(
const QPainterPath &other)
2382 return *
this = (*
this & other);
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);
2411
2412
2413
2414
2415
2416
2417
2418QPainterPath &QPainterPath::operator-=(
const QPainterPath &other)
2420 return *
this = (*
this - other);
2423#ifndef QT_NO_DATASTREAM
2425
2426
2427
2428
2429
2430
2431
2432
2440 s << p.elementCount();
2441 for (
int i=0; i < p.d_func()->elements.size(); ++i) {
2442 const QPainterPath::Element &e = p.d_func()->elements.at(i);
2444 s <<
double(e.x) <<
double(e.y);
2446 s << p.d_func()->cStart;
2447 s <<
int(p.fillRule());
2452
2453
2454
2455
2456
2457
2458
2459
2462 bool errorDetected =
false;
2473 p.d_func()->elements.clear();
2474 for (
int i=0; i<size; ++i) {
2480 Q_ASSERT(type >= 0 && type <= 3);
2481 if (!isValidCoord(qreal(x)) || !isValidCoord(qreal(y))) {
2483 qWarning(
"QDataStream::operator>>: Invalid QPainterPath coordinates read, skipping it");
2485 errorDetected =
true;
2488 QPainterPath::Element elm = { qreal(x), qreal(y), QPainterPath::ElementType(type) };
2489 p.d_func()->elements.append(elm);
2491 s >> p.d_func()->cStart;
2494 Q_ASSERT(fillRule == Qt::OddEvenFill || fillRule == Qt::WindingFill);
2495 p.d_func()->hasWindingFill = (Qt::FillRule(fillRule) == Qt::WindingFill);
2496 if (errorDetected || p.d_func()->elements.isEmpty())
2504
2505
2509 ((QPainterPath *) data)->moveTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
2514 ((QPainterPath *) data)->lineTo(qt_fixed_to_real(x), qt_fixed_to_real(y));
2518 qfixed c2x, qfixed c2y,
2519 qfixed ex, qfixed ey,
2522 ((QPainterPath *) data)->cubicTo(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y),
2523 qt_fixed_to_real(c2x), qt_fixed_to_real(c2y),
2524 qt_fixed_to_real(ex), qt_fixed_to_real(ey));
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
2571
2572
2573
2574
2575
2576
2581 stroker.setMoveToHook(qt_path_stroke_move_to);
2582 stroker.setLineToHook(qt_path_stroke_line_to);
2583 stroker.setCubicToHook(qt_path_stroke_cubic_to);
2587
2588
2589QPainterPathStroker::QPainterPathStroker()
2590 : d_ptr(
new QPainterPathStrokerPrivate)
2595
2596
2597
2598
2599QPainterPathStroker::QPainterPathStroker(
const QPen &pen)
2600 : d_ptr(
new QPainterPathStrokerPrivate)
2602 setWidth(pen.widthF());
2603 setCapStyle(pen.capStyle());
2604 setJoinStyle(pen.joinStyle());
2605 setMiterLimit(pen.miterLimit());
2606 setDashOffset(pen.dashOffset());
2608 if (pen.style() == Qt::CustomDashLine)
2609 setDashPattern(pen.dashPattern());
2611 setDashPattern(pen.style());
2615
2616
2617QPainterPathStroker::~QPainterPathStroker()
2623
2624
2625
2626
2627
2628
2629
2630
2631
2632
2633
2634
2635QPainterPath QPainterPathStroker::createStroke(
const QPainterPath &path)
const
2637 QPainterPathStrokerPrivate *d =
const_cast<QPainterPathStrokerPrivate *>(d_func());
2638 QPainterPath stroke;
2641 if (d->dashPattern.isEmpty()) {
2642 d->stroker.strokePath(path, &stroke, QTransform());
2644 QDashStroker dashStroker(&d->stroker);
2645 dashStroker.setDashPattern(d->dashPattern);
2646 dashStroker.setDashOffset(d->dashOffset);
2647 dashStroker.setClipRect(d->stroker.clipRect());
2648 dashStroker.strokePath(path, &stroke, QTransform());
2650 stroke.setFillRule(Qt::WindingFill);
2655
2656
2657
2658
2659
2660void QPainterPathStroker::setWidth(qreal width)
2662 Q_D(QPainterPathStroker);
2665 d->stroker.setStrokeWidth(qt_real_to_fixed(width));
2669
2670
2671qreal QPainterPathStroker::width()
const
2673 return qt_fixed_to_real(d_func()->stroker.strokeWidth());
2678
2679
2680
2681
2682void QPainterPathStroker::setCapStyle(Qt::PenCapStyle style)
2684 d_func()->stroker.setCapStyle(style);
2689
2690
2691Qt::PenCapStyle QPainterPathStroker::capStyle()
const
2693 return d_func()->stroker.capStyle();
2697
2698
2699void QPainterPathStroker::setJoinStyle(Qt::PenJoinStyle style)
2701 d_func()->stroker.setJoinStyle(style);
2705
2706
2707Qt::PenJoinStyle QPainterPathStroker::joinStyle()
const
2709 return d_func()->stroker.joinStyle();
2713
2714
2715
2716
2717
2718
2719
2720
2721
2722void QPainterPathStroker::setMiterLimit(qreal limit)
2724 d_func()->stroker.setMiterLimit(qt_real_to_fixed(limit));
2728
2729
2730qreal QPainterPathStroker::miterLimit()
const
2732 return qt_fixed_to_real(d_func()->stroker.miterLimit());
2737
2738
2739
2740
2741
2742
2743
2744void QPainterPathStroker::setCurveThreshold(qreal threshold)
2746 d_func()->stroker.setCurveThreshold(qt_real_to_fixed(threshold));
2750
2751
2752
2753qreal QPainterPathStroker::curveThreshold()
const
2755 return qt_fixed_to_real(d_func()->stroker.curveThreshold());
2759
2760
2761void QPainterPathStroker::setDashPattern(Qt::PenStyle style)
2763 d_func()->dashPattern = QDashStroker::patternForStyle(style);
2767
2768
2769
2770
2771
2772
2773
2774
2775
2776
2777
2778
2779
2780
2781
2782void QPainterPathStroker::setDashPattern(
const QList<qreal> &dashPattern)
2784 d_func()->dashPattern.clear();
2785 for (
int i=0; i<dashPattern.size(); ++i)
2786 d_func()->dashPattern << qt_real_to_fixed(dashPattern.at(i));
2790
2791
2792QList<qreal> QPainterPathStroker::dashPattern()
const
2794 return d_func()->dashPattern;
2798
2799
2800qreal QPainterPathStroker::dashOffset()
const
2802 return d_func()->dashOffset;
2806
2807
2808
2809
2810
2811void QPainterPathStroker::setDashOffset(qreal offset)
2813 d_func()->dashOffset = offset;
2817
2818
2819
2820
2821
2822
2823
2824
2825
2826
2827
2828
2829
2830
2831QPolygonF QPainterPath::toFillPolygon(
const QTransform &matrix)
const
2833 const QList<QPolygonF> flats = toSubpathPolygons(matrix);
2835 if (flats.isEmpty())
2837 QPointF first = flats.first().first();
2838 for (
int i=0; i<flats.size(); ++i) {
2839 polygon += flats.at(i);
2840 if (!flats.at(i).isClosed())
2841 polygon += flats.at(i).first();
2849
2850
2851
2852
2853
2854bool QPainterPath::isCachingEnabled()
const
2857 return d && d->cacheEnabled;
2861
2862
2863
2864
2865
2866
2867
2868
2869
2870
2871
2872void QPainterPath::setCachingEnabled(
bool enabled)
2875 if (d_func()->cacheEnabled == enabled)
2878 QPainterPathPrivate *d = d_func();
2879 d->cacheEnabled = enabled;
2881 d->m_runLengths.clear();
2882 d->m_runLengths.squeeze();
2889 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a);
2893
2894
2895qreal QPainterPath::length()
const
2900 if (d->cacheEnabled) {
2901 if (d->dirtyRunLengths)
2902 d->computeRunLengths();
2903 return d->m_runLengths.last();
2907 for (
int i=1; i<d->elements.size(); ++i) {
2908 const Element &e = d->elements.at(i);
2915 len += QLineF(d->elements.at(i-1), e).length();
2918 case CurveToElement:
2920 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
2922 d->elements.at(i+1),
2923 d->elements.at(i+2));
2936
2937
2938
2939
2940
2941
2942
2943qreal QPainterPath::percentAtLength(qreal len)
const
2946 if (isEmpty() || len <= 0)
2949 qreal totalLength = length();
2950 if (len > totalLength)
2953 Q_ASSERT(totalLength != 0);
2955 if (d->cacheEnabled) {
2956 const int ei = qMax(d->elementAtT(len / totalLength), 1);
2958 const QPainterPath::Element &e = d->elements[ei];
2960 case QPainterPath::LineToElement:
2961 res = len / totalLength;
2963 case CurveToElement:
2965 QBezier b = QBezier::fromPoints(d->elements.at(ei-1),
2967 d->elements.at(ei+1),
2968 d->elements.at(ei+2));
2969 qreal prevLen = d->m_runLengths[ei - 1];
2970 qreal blen = d->m_runLengths[ei] - prevLen;
2971 qreal elemRes = b.tAtLength(len - prevLen);
2972 res = (elemRes * blen + prevLen) / totalLength;
2982 for (
int i=1; i<d->elements.size(); ++i) {
2983 const Element &e = d->elements.at(i);
2990 QLineF line(d->elements.at(i-1), e);
2991 qreal llen = line.length();
2993 if (curLen >= len) {
2994 return len/totalLength ;
2999 case CurveToElement:
3001 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
3003 d->elements.at(i+1),
3004 d->elements.at(i+2));
3005 qreal blen = b.length();
3006 qreal prevLen = curLen;
3009 if (curLen >= len) {
3010 qreal res = b.tAtLength(len - prevLen);
3011 return (res * blen + prevLen)/totalLength;
3026 qreal *bezierLength)
3028 *startingLength = 0;
3033 qreal totalLength = path.length();
3035 const int lastElement = path.elementCount() - 1;
3036 for (
int i=0; i <= lastElement; ++i) {
3037 const QPainterPath::Element &e = path.elementAt(i);
3040 case QPainterPath::MoveToElement:
3042 case QPainterPath::LineToElement:
3044 QLineF line(path.elementAt(i-1), e);
3045 qreal llen = line.length();
3047 if (i == lastElement || curLen/totalLength >= t) {
3048 *bezierLength = llen;
3049 QPointF a = path.elementAt(i-1);
3050 QPointF delta = e - a;
3051 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e);
3055 case QPainterPath::CurveToElement:
3057 QBezier b = QBezier::fromPoints(path.elementAt(i-1),
3059 path.elementAt(i+1),
3060 path.elementAt(i+2));
3061 qreal blen = b.length();
3064 if (i + 2 == lastElement || curLen/totalLength >= t) {
3065 *bezierLength = blen;
3075 *startingLength = curLen;
3081 qreal *bezierLength)
const
3083 Q_ASSERT(t >= 0 && t <= 1);
3085 if (!path.isEmpty() && d->cacheEnabled) {
3086 const int ei = qMax(d->elementAtT(t), 1);
3087 const qreal prevRunLength = d->m_runLengths[ei - 1];
3088 *startingLength = prevRunLength;
3089 *bezierLength = d->m_runLengths[ei] - prevRunLength;
3090 const QPointF prev = d->elements[ei - 1];
3091 const QPainterPath::Element &e = d->elements[ei];
3093 case QPainterPath::LineToElement:
3095 QPointF delta = (e - prev) / 3;
3096 return QBezier::fromPoints(prev, prev + delta, prev + 2 * delta, e);
3098 case QPainterPath::CurveToElement:
3099 return QBezier::fromPoints(prev, e, elements[ei + 1], elements[ei + 2]);
3106 return uncached_bezierAtT(path, t, startingLength, bezierLength);
3110
3111
3112
3113
3114
3115
3116
3117
3118QPointF QPainterPath::pointAtPercent(qreal t)
const
3120 if (t < 0 || t > 1) {
3121 qWarning(
"QPainterPath::pointAtPercent accepts only values between 0 and 1");
3125 if (!d_ptr || d_ptr->elements.size() == 0)
3128 if (d_ptr->elements.size() == 1)
3129 return d_ptr->elements.at(0);
3131 qreal totalLength = length();
3133 qreal bezierLen = 0;
3134 QBezier b = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3135 Q_ASSERT(bezierLen != 0);
3136 qreal realT = (totalLength * t - curLen) / bezierLen;
3138 return b.pointAt(qBound(qreal(0), realT, qreal(1)));
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153qreal QPainterPath::angleAtPercent(qreal t)
const
3155 if (t < 0 || t > 1) {
3156 qWarning(
"QPainterPath::angleAtPercent accepts only values between 0 and 1");
3163 qreal totalLength = length();
3165 qreal bezierLen = 0;
3166 QBezier bez = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3167 Q_ASSERT(bezierLen != 0);
3168 qreal realT = (totalLength * t - curLen) / bezierLen;
3170 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3171 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3173 return QLineF(0, 0, m1, m2).angle();
3178
3179
3180
3181
3182
3183
3184
3185
3186qreal QPainterPath::slopeAtPercent(qreal t)
const
3188 if (t < 0 || t > 1) {
3189 qWarning(
"QPainterPath::slopeAtPercent accepts only values between 0 and 1");
3196 qreal totalLength = length();
3198 qreal bezierLen = 0;
3199 QBezier bez = d_ptr->bezierAtT(*
this, t, &curLen, &bezierLen);
3200 Q_ASSERT(bezierLen != 0);
3201 qreal realT = (totalLength * t - curLen) / bezierLen;
3203 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4);
3204 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4);
3211 if (std::numeric_limits<qreal>::has_infinity) {
3212 slope = (m2 < 0) ? -std::numeric_limits<qreal>::infinity()
3213 : std::numeric_limits<qreal>::infinity();
3215 if (
sizeof(qreal) ==
sizeof(
double)) {
3216 return 1.79769313486231570e+308;
3218 return ((qreal)3.40282346638528860e+38);
3227
3228
3229
3230
3231
3232
3233
3234
3235
3236
3237
3238
3239
3240
3241
3243QPainterPath QPainterPath::trimmed(qreal fromFraction, qreal toFraction, qreal offset)
const
3249 if (!isCachingEnabled()) {
3250 QPainterPath copy(*
this);
3251 copy.setCachingEnabled(
true);
3252 return copy.trimmed(fromFraction, toFraction, offset);
3255 qreal f1 = qBound(qreal(0), fromFraction, qreal(1));
3256 qreal f2 = qBound(qreal(0), toFraction, qreal(1));
3257 if (qFuzzyIsNull(f1 - f2))
3258 return QPainterPath();
3261 if (qFuzzyCompare(f2 - f1, qreal(1)))
3265 res.setFillRule(fillRule());
3269 offset = std::modf(offset, &dummy);
3271 qreal of1 = f1 + offset;
3272 qreal of2 = f2 + offset;
3274 f1 = of1 < 0 ? of1 + 1 : of1;
3275 f2 = of2 + 1 > 1 ? of2 : of2 + 1;
3276 }
else if (offset > 0) {
3277 f1 = of1 - 1 < 0 ? of1 : of1 - 1;
3278 f2 = of2 > 1 ? of2 - 1 : of2;
3281 const bool wrapping = (f1 > f2);
3284 QPainterPathPrivate *d = d_func();
3285 if (d->dirtyRunLengths)
3286 d->computeRunLengths();
3287 const qreal totalLength = d->m_runLengths.last();
3288 if (qFuzzyIsNull(totalLength))
3291 const qreal l1 = f1 * totalLength;
3292 const qreal l2 = f2 * totalLength;
3293 const int e1 = d->elementAtLength(l1);
3294 const bool mustTrimE1 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e1), l1);
3295 const int e2 = d->elementAtLength(l2);
3296 const bool mustTrimE2 = !QtPrivate::fuzzyCompare(d->m_runLengths.at(e2), l2);
3299 if (e1 == e2 && !wrapping && mustTrimE1 && mustTrimE2) {
3301 d->appendSliceOfElement(&res, e1, l1, l2);
3305 d->appendEndOfElement(&res, e1, l1);
3307 res.moveTo(d->endPointOfElement(e1));
3310 int firstWholeElement = e1 + 1;
3311 int lastWholeElement = (mustTrimE2 ? e2 - 1 : e2);
3313 d->appendElementRange(&res, firstWholeElement, lastWholeElement);
3315 int lastIndex = d->elements.size() - 1;
3316 d->appendElementRange(&res, firstWholeElement, lastIndex);
3317 bool isClosed = (QPointF(d->elements.at(0)) == QPointF(d->elements.at(lastIndex)));
3319 d->appendElementRange(&res, (isClosed ? 1 : 0), lastWholeElement);
3324 d->appendStartOfElement(&res, e2, l2);
3331 qreal startLen, qreal endLen)
3333 Q_ASSERT(cacheEnabled);
3334 Q_ASSERT(!dirtyRunLengths);
3336 if (elemIdx <= 0 || elemIdx >= elements.size())
3339 const qreal prevLen = m_runLengths.at(elemIdx - 1);
3340 const qreal elemLen = m_runLengths.at(elemIdx) - prevLen;
3341 const qreal len1 = startLen - prevLen;
3342 const qreal len2 = endLen - prevLen;
3343 if (qFuzzyIsNull(elemLen))
3346 const QPointF pp = elements.at(elemIdx - 1);
3347 const QPainterPath::Element e = elements.at(elemIdx);
3350 QPointF p1 = (trimFlags & TrimStart) ? l.pointAt(len1 / elemLen) : pp;
3351 QPointF p2 = (trimFlags & TrimEnd) ? l.pointAt(len2 / elemLen) : e;
3355 }
else if (e.isCurveTo()) {
3356 Q_ASSERT(elemIdx < elements.size() - 2);
3357 QBezier b = QBezier::fromPoints(pp, e, elements.at(elemIdx + 1), elements.at(elemIdx + 2));
3358 qreal t1 = (trimFlags & TrimStart) ? b.tAtLength(len1) : 0.0;
3359 qreal t2 = (trimFlags & TrimEnd) ? b.tAtLength(len2) : 1.0;
3360 QBezier c = b.getSubRange(t1, t2);
3362 to->moveTo(c.pt1());
3363 to->cubicTo(c.pt2(), c.pt3(), c.pt4());
3371 if (first < 0 || first >= elements.size() || last < 0 || last >= elements.size())
3375 for (
int i = first; i <= last; i++) {
3376 const QPainterPath::Element &e = elements.at(i);
3378 case QPainterPath::MoveToElement:
3381 case QPainterPath::LineToElement:
3384 case QPainterPath::CurveToElement:
3385 Q_ASSERT(i < elements.size() - 2);
3386 to->cubicTo(e, elements.at(i + 1), elements.at(i + 2));
3398
3399
3400
3401
3402
3403
3404
3405
3406
3407
3408
3409
3410void QPainterPath::addRoundedRect(
const QRectF &rect, qreal xRadius, qreal yRadius,
3413 QRectF r = rect.normalized();
3418 if (mode == Qt::AbsoluteSize) {
3419 qreal w = r.width() / 2;
3420 qreal h = r.height() / 2;
3425 xRadius = 100 * qMin(xRadius, w) / w;
3430 yRadius = 100 * qMin(yRadius, h) / h;
3440 if (xRadius <= 0 || yRadius <= 0) {
3447 qreal w = r.width();
3448 qreal h = r.height();
3449 qreal rxx2 = w*xRadius/100;
3450 qreal ryy2 = h*yRadius/100;
3455 bool first = d_func()->elements.size() < 2;
3457 arcMoveTo(x, y, rxx2, ryy2, 180);
3458 arcTo(x, y, rxx2, ryy2, 180, -90);
3459 arcTo(x+w-rxx2, y, rxx2, ryy2, 90, -90);
3460 arcTo(x+w-rxx2, y+h-ryy2, rxx2, ryy2, 0, -90);
3461 arcTo(x, y+h-ryy2, rxx2, ryy2, 270, -90);
3464 d_func()->require_moveTo =
true;
3465 d_func()->convex = first;
3469
3470
3471
3472
3473
3474
3477
3478
3479
3480
3481
3482
3483
3484
3485
3486
3487
3488QPainterPath QPainterPath::united(
const QPainterPath &p)
const
3490 if (isEmpty() || p.isEmpty())
3491 return isEmpty() ? p : *
this;
3492 QPathClipper clipper(*
this, p);
3493 return clipper.clip(QPathClipper::BoolOr);
3497
3498
3499
3500
3501
3502
3503QPainterPath QPainterPath::intersected(
const QPainterPath &p)
const
3505 if (isEmpty() || p.isEmpty())
3506 return QPainterPath();
3507 QPathClipper clipper(*
this, p);
3508 return clipper.clip(QPathClipper::BoolAnd);
3512
3513
3514
3515
3516
3517
3518
3519
3520
3521QPainterPath QPainterPath::subtracted(
const QPainterPath &p)
const
3523 if (isEmpty() || p.isEmpty())
3525 QPathClipper clipper(*
this, p);
3526 return clipper.clip(QPathClipper::BoolSub);
3530
3531
3532
3533
3534
3535
3536
3537
3538QPainterPath QPainterPath::simplified()
const
3542 QPathClipper clipper(*
this, QPainterPath());
3543 return clipper.clip(QPathClipper::Simplify);
3547
3548
3549
3550
3551
3552
3553
3554
3555
3556
3557bool QPainterPath::intersects(
const QPainterPath &p)
const
3559 if (p.elementCount() == 1)
3560 return contains(p.elementAt(0));
3561 if (isEmpty() || p.isEmpty())
3563 QPathClipper clipper(*
this, p);
3564 return clipper.intersect();
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
3579bool QPainterPath::contains(
const QPainterPath &p)
const
3581 if (p.elementCount() == 1)
3582 return contains(p.elementAt(0));
3583 if (isEmpty() || p.isEmpty())
3585 QPathClipper clipper(*
this, p);
3586 return clipper.contains();
3589void QPainterPath::setDirty(
bool dirty)
3591 d_func()->pathConverter.reset();
3592 d_func()->dirtyBounds = dirty;
3593 d_func()->dirtyControlBounds = dirty;
3594 d_func()->dirtyRunLengths = dirty;
3595 d_func()->convex =
false;
3598void QPainterPath::computeBoundingRect()
const
3600 QPainterPathPrivate *d = d_func();
3601 d->dirtyBounds =
false;
3603 d->bounds = QRect();
3607 qreal minx, maxx, miny, maxy;
3608 minx = maxx = d->elements.at(0).x;
3609 miny = maxy = d->elements.at(0).y;
3610 for (
int i=1; i<d->elements.size(); ++i) {
3611 const Element &e = d->elements.at(i);
3616 if (e.x > maxx) maxx = e.x;
3617 else if (e.x < minx) minx = e.x;
3618 if (e.y > maxy) maxy = e.y;
3619 else if (e.y < miny) miny = e.y;
3621 case CurveToElement:
3623 QBezier b = QBezier::fromPoints(d->elements.at(i-1),
3625 d->elements.at(i+1),
3626 d->elements.at(i+2));
3627 QRectF r = qt_painterpath_bezier_extrema(b);
3628 qreal right = r.right();
3629 qreal bottom = r.bottom();
3630 if (r.x() < minx) minx = r.x();
3631 if (right > maxx) maxx = right;
3632 if (r.y() < miny) miny = r.y();
3633 if (bottom > maxy) maxy = bottom;
3641 d->bounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3645void QPainterPath::computeControlPointRect()
const
3647 QPainterPathPrivate *d = d_func();
3648 d->dirtyControlBounds =
false;
3650 d->controlBounds = QRect();
3654 qreal minx, maxx, miny, maxy;
3655 minx = maxx = d->elements.at(0).x;
3656 miny = maxy = d->elements.at(0).y;
3657 for (
int i=1; i<d->elements.size(); ++i) {
3658 const Element &e = d->elements.at(i);
3659 if (e.x > maxx) maxx = e.x;
3660 else if (e.x < minx) minx = e.x;
3661 if (e.y > maxy) maxy = e.y;
3662 else if (e.y < miny) miny = e.y;
3664 d->controlBounds = QRectF(minx, miny, maxx - minx, maxy - miny);
3669 Q_ASSERT(!elements.isEmpty());
3671 m_runLengths.clear();
3672 const int numElems = elements.size();
3673 m_runLengths.reserve(numElems);
3675 QPointF runPt = elements[0];
3677 for (
int i = 0; i < numElems; i++) {
3678 QPainterPath::Element e = elements[i];
3680 case QPainterPath::LineToElement:
3681 runLen += QLineF(runPt, e).length();
3684 case QPainterPath::CurveToElement: {
3685 Q_ASSERT(i < numElems - 2);
3686 QPainterPath::Element ee = elements[i + 2];
3687 runLen += QBezier::fromPoints(runPt, e, elements[i + 1], ee).length();
3691 case QPainterPath::MoveToElement:
3694 case QPainterPath::CurveToDataElement:
3697 m_runLengths.append(runLen);
3699 Q_ASSERT(m_runLengths.size() == elements.size());
3701 dirtyRunLengths =
false;
3704#ifndef QT_NO_DEBUG_STREAM
3707 QDebugStateSaver saver(s);
3708 s.nospace() <<
"QPainterPath: Element count=" << p.elementCount() << Qt::endl;
3709 const char *types[] = {
"MoveTo",
"LineTo",
"CurveTo",
"CurveToData"};
3710 for (
int i=0; i<p.elementCount(); ++i) {
3711 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)