200void QStrokerOps::strokePath(
const QPainterPath &path,
void *customData,
const QTransform &matrix)
205 setCurveThresholdFromTransform(QTransform());
207 int count = path.elementCount();
208 if (matrix.isIdentity()) {
209 for (
int i=0; i<count; ++i) {
210 const QPainterPath::Element &e = path.elementAt(i);
212 case QPainterPath::MoveToElement:
213 moveTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y));
215 case QPainterPath::LineToElement:
216 lineTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y));
218 case QPainterPath::CurveToElement:
220 const QPainterPath::Element &cp2 = path.elementAt(++i);
221 const QPainterPath::Element &ep = path.elementAt(++i);
222 cubicTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y),
223 qt_real_to_fixed(cp2.x), qt_real_to_fixed(cp2.y),
224 qt_real_to_fixed(ep.x), qt_real_to_fixed(ep.y));
232 for (
int i=0; i<count; ++i) {
233 const QPainterPath::Element &e = path.elementAt(i);
234 QPointF pt = QPointF(e.x, e.y) * matrix;
236 case QPainterPath::MoveToElement:
237 moveTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
239 case QPainterPath::LineToElement:
240 lineTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
242 case QPainterPath::CurveToElement:
244 QPointF cp2 = ((QPointF) path.elementAt(++i)) * matrix;
245 QPointF ep = ((QPointF) path.elementAt(++i)) * matrix;
246 cubicTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()),
247 qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
248 qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
401void QStroker::joinPoints(qfixed focal_x, qfixed focal_y,
const QLineF &nextLine, LineJoinMode join)
403#ifdef QPP_STROKE_DEBUG
404 printf(
" -----> joinPoints: around=(%.0f, %.0f), next_p1=(%.0f, %.f) next_p2=(%.0f, %.f)\n",
405 qt_fixed_to_real(focal_x),
406 qt_fixed_to_real(focal_y),
407 nextLine.x1(), nextLine.y1(), nextLine.x2(), nextLine.y2());
411#if !defined (QFIXED_26_6) && !defined (Q_FIXED_32_32)
412 if (qFuzzyCompare(m_back1X, nextLine.x1()) && qFuzzyCompare(m_back1Y, nextLine.y1()))
415 if (m_back1X == qt_real_to_fixed(nextLine.x1())
416 && m_back1Y == qt_real_to_fixed(nextLine.y1())) {
420 QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y),
421 qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y));
423 QLineF::IntersectionType type = prevLine.intersects(nextLine, &isect);
425 if (join == FlatJoin) {
426 QLineF shortCut(prevLine.p2(), nextLine.p1());
427 qreal angle = shortCut.angleTo(prevLine);
428 if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
429 emitLineTo(focal_x, focal_y);
430 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
433 emitLineTo(qt_real_to_fixed(nextLine.x1()),
434 qt_real_to_fixed(nextLine.y1()));
437 if (join == MiterJoin) {
438 qreal appliedMiterLimit = qt_fixed_to_real(m_strokeWidth * m_miterLimit);
441 QLineF shortCut(prevLine.p2(), nextLine.p1());
442 qreal angle = shortCut.angleTo(prevLine);
443 if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
444 emitLineTo(focal_x, focal_y);
445 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
448 QLineF miterLine(QPointF(qt_fixed_to_real(m_back1X),
449 qt_fixed_to_real(m_back1Y)), isect);
450 if (type == QLineF::NoIntersection || miterLine.length() > appliedMiterLimit) {
452 l1.setLength(appliedMiterLimit);
453 l1.translate(prevLine.dx(), prevLine.dy());
456 l2.setLength(appliedMiterLimit);
457 l2.translate(-l2.dx(), -l2.dy());
459 emitLineTo(qt_real_to_fixed(l1.x2()), qt_real_to_fixed(l1.y2()));
460 emitLineTo(qt_real_to_fixed(l2.x1()), qt_real_to_fixed(l2.y1()));
461 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
463 emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
464 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
467 }
else if (join == SquareJoin) {
468 qfixed offset = m_strokeWidth / 2;
471 qreal dp = QPointF::dotProduct(QPointF(prevLine.dx(), prevLine.dy()), QPointF(nextLine.dx(), nextLine.dy()));
473 l1 = QLineF(prevLine.p2(), prevLine.p1());
475 l1.translate(l1.dx(), l1.dy());
476 l1.setLength(qt_fixed_to_real(offset));
477 QLineF l2(nextLine.p2(), nextLine.p1());
478 l2.translate(l2.dx(), l2.dy());
479 l2.setLength(qt_fixed_to_real(offset));
480 emitLineTo(qt_real_to_fixed(l1.x2()), qt_real_to_fixed(l1.y2()));
481 emitLineTo(qt_real_to_fixed(l2.x2()), qt_real_to_fixed(l2.y2()));
482 emitLineTo(qt_real_to_fixed(l2.x1()), qt_real_to_fixed(l2.y1()));
484 }
else if (join == RoundJoin) {
485 qfixed offset = m_strokeWidth / 2;
487 QLineF shortCut(prevLine.p2(), nextLine.p1());
488 qreal angle = shortCut.angleTo(prevLine);
489 if ((type == QLineF::BoundedIntersection || (angle > qreal(90.01))) && nextLine.length() > offset) {
490 emitLineTo(focal_x, focal_y);
491 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
494 qreal l1_on_x = adapted_angle_on_x(prevLine);
495 qreal l2_on_x = adapted_angle_on_x(nextLine);
497 qreal sweepLength = qAbs(l2_on_x - l1_on_x);
502 QPointF curve_start =
503 qt_curves_for_arc(QRectF(qt_fixed_to_real(focal_x - offset),
504 qt_fixed_to_real(focal_y - offset),
505 qt_fixed_to_real(offset * 2),
506 qt_fixed_to_real(offset * 2)),
507 l1_on_x + 90, -sweepLength,
508 curves, &point_count);
512 Q_UNUSED(curve_start);
514 for (
int i=0; i<point_count; i+=3) {
515 emitCubicTo(qt_real_to_fixed(curves[i].x()),
516 qt_real_to_fixed(curves[i].y()),
517 qt_real_to_fixed(curves[i+1].x()),
518 qt_real_to_fixed(curves[i+1].y()),
519 qt_real_to_fixed(curves[i+2].x()),
520 qt_real_to_fixed(curves[i+2].y()));
524 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
528 }
else if (join == RoundCap) {
529 qfixed offset = m_strokeWidth / 2;
532 QLineF l1 = prevLine;
533 qreal dp = QPointF::dotProduct(QPointF(prevLine.dx(), prevLine.dy()), QPointF(nextLine.dx(), nextLine.dy()));
535 l1 = QLineF(prevLine.p2(), prevLine.p1());
537 l1.translate(l1.dx(), l1.dy());
538 l1.setLength(QT_PATH_KAPPA * offset);
541 QLineF l2(qt_fixed_to_real(focal_x), qt_fixed_to_real(focal_y),
542 prevLine.x2(), prevLine.y2());
543 l2.translate(-l2.dy(), l2.dx());
544 l2.setLength(QT_PATH_KAPPA * offset);
546 emitCubicTo(qt_real_to_fixed(l1.x2()),
547 qt_real_to_fixed(l1.y2()),
548 qt_real_to_fixed(l2.x2()),
549 qt_real_to_fixed(l2.y2()),
550 qt_real_to_fixed(l2.x1()),
551 qt_real_to_fixed(l2.y1()));
554 l2 = QLineF(l2.x1(), l2.y1(), l2.x1()-l2.dx(), l2.y1()-l2.dy());
557 l1.translate(nextLine.x1() - l1.x1(), nextLine.y1() - l1.y1());
559 emitCubicTo(qt_real_to_fixed(l2.x2()),
560 qt_real_to_fixed(l2.y2()),
561 qt_real_to_fixed(l1.x2()),
562 qt_real_to_fixed(l1.y2()),
563 qt_real_to_fixed(l1.x1()),
564 qt_real_to_fixed(l1.y1()));
565 }
else if (join == SvgMiterJoin) {
566 QLineF shortCut(prevLine.p2(), nextLine.p1());
567 qreal angle = shortCut.angleTo(prevLine);
568 if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
569 emitLineTo(focal_x, focal_y);
570 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
573 QLineF miterLine(QPointF(qt_fixed_to_real(focal_x),
574 qt_fixed_to_real(focal_y)), isect);
575 if (type == QLineF::NoIntersection || miterLine.length() > qt_fixed_to_real(m_strokeWidth * m_miterLimit) / 2) {
576 emitLineTo(qt_real_to_fixed(nextLine.x1()),
577 qt_real_to_fixed(nextLine.y1()));
579 emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
580 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
583 Q_ASSERT(!
"QStroker::joinPoints(), bad join style...");
599 QLineF *startTangent)
602 const int MAX_OFFSET = 16;
603 QBezier offsetCurves[MAX_OFFSET];
605 Q_ASSERT(it->hasNext());
606 QStrokerOps::Element first_element = it->next();
607 Q_ASSERT(first_element.isMoveTo());
609 qfixed2d start = first_element;
611#ifdef QPP_STROKE_DEBUG
612 qDebug(
" -> (side) [%.2f, %.2f], startPos=%d",
613 qt_fixed_to_real(start.x),
614 qt_fixed_to_real(start.y));
617 qfixed2d prev = start;
621 qfixed offset = stroker->strokeWidth() / 2;
623 while (it->hasNext()) {
624 QStrokerOps::Element e = it->next();
628#ifdef QPP_STROKE_DEBUG
629 qDebug(
"\n ---> (side) lineto [%.2f, %.2f]", e.x, e.y);
631 QLineF line(qt_fixed_to_real(prev.x), qt_fixed_to_real(prev.y),
632 qt_fixed_to_real(e.x), qt_fixed_to_real(e.y));
633 if (line.p1() != line.p2()) {
634 QLineF normal = line.normalVector();
635 normal.setLength(offset);
636 line.translate(normal.dx(), normal.dy());
641 stroker->joinPoints(prev.x, prev.y, line, stroker->capStyleMode());
643 stroker->emitMoveTo(qt_real_to_fixed(line.x1()), qt_real_to_fixed(line.y1()));
644 *startTangent = line;
647 stroker->joinPoints(prev.x, prev.y, line, stroker->joinStyleMode());
651 stroker->emitLineTo(qt_real_to_fixed(line.x2()),
652 qt_real_to_fixed(line.y2()));
657 }
else if (e.isCurveTo()) {
658 QStrokerOps::Element cp2 = it->next();
659 QStrokerOps::Element ep = it->next();
661#ifdef QPP_STROKE_DEBUG
662 qDebug(
"\n ---> (side) cubicTo [%.2f, %.2f]",
663 qt_fixed_to_real(ep.x),
664 qt_fixed_to_real(ep.y));
668 QBezier::fromPoints(QPointF(qt_fixed_to_real(prev.x), qt_fixed_to_real(prev.y)),
669 QPointF(qt_fixed_to_real(e.x), qt_fixed_to_real(e.y)),
670 QPointF(qt_fixed_to_real(cp2.x), qt_fixed_to_real(cp2.y)),
671 QPointF(qt_fixed_to_real(ep.x), qt_fixed_to_real(ep.y)));
672 int count = bezier.shifted(offsetCurves,
675 stroker->curveThreshold());
679 QLineF tangent = bezier.startTangent();
680 tangent.translate(offsetCurves[0].pt1() - bezier.pt1());
682 QPointF pt = offsetCurves[0].pt1();
684 stroker->joinPoints(prev.x, prev.y,
686 stroker->capStyleMode());
688 stroker->emitMoveTo(qt_real_to_fixed(pt.x()),
689 qt_real_to_fixed(pt.y()));
691 *startTangent = tangent;
694 stroker->joinPoints(prev.x, prev.y,
696 stroker->joinStyleMode());
700 for (
int i=0; i<count; ++i) {
701 QPointF cp1 = offsetCurves[i].pt2();
702 QPointF cp2 = offsetCurves[i].pt3();
703 QPointF ep = offsetCurves[i].pt4();
704 stroker->emitCubicTo(qt_real_to_fixed(cp1.x()), qt_real_to_fixed(cp1.y()),
705 qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
706 qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
714 if (start == prev && !stroker->forceOpen()) {
716#ifdef QPP_STROKE_DEBUG
717 qDebug(
"\n ---> (side) closed subpath");
721 stroker->joinPoints(prev.x, prev.y, *startTangent, stroker->joinStyleMode());
724#ifdef QPP_STROKE_DEBUG
725 qDebug(
"\n ---> (side) open subpath");
817 QPointF *curves,
int *point_count)
819 Q_ASSERT(point_count);
823 if (qt_is_nan(rect.x()) || qt_is_nan(rect.y()) || qt_is_nan(rect.width()) || qt_is_nan(rect.height())
824 || qt_is_nan(startAngle) || qt_is_nan(sweepLength)) {
825 qWarning(
"QPainterPath::arcTo: Adding arc where a parameter is NaN, results are undefined");
836 qreal w = rect.width();
837 qreal w2 = rect.width() / 2;
838 qreal w2k = w2 * QT_PATH_KAPPA;
840 qreal h = rect.height();
841 qreal h2 = rect.height() / 2;
842 qreal h2k = h2 * QT_PATH_KAPPA;
847 QPointF(x + w, y + h2),
850 QPointF(x + w, y + h2 + h2k),
851 QPointF(x + w2 + w2k, y + h),
852 QPointF(x + w2, y + h),
855 QPointF(x + w2 - w2k, y + h),
856 QPointF(x, y + h2 + h2k),
860 QPointF(x, y + h2 - h2k),
861 QPointF(x + w2 - w2k, y),
865 QPointF(x + w2 + w2k, y),
866 QPointF(x + w, y + h2 - h2k),
867 QPointF(x + w, y + h2)
870 if (sweepLength > 360) sweepLength = 360;
871 else if (sweepLength < -360) sweepLength = -360;
874 if (startAngle == 0.0) {
875 if (sweepLength == 360.0) {
876 for (
int i = 11; i >= 0; --i)
877 curves[(*point_count)++] = points[i];
879 }
else if (sweepLength == -360.0) {
880 for (
int i = 1; i <= 12; ++i)
881 curves[(*point_count)++] = points[i];
886 int startSegment =
int(qFloor(startAngle / 90));
887 int endSegment =
int(qFloor((startAngle + sweepLength) / 90));
889 qreal startT = (startAngle - startSegment * 90) / 90;
890 qreal endT = (startAngle + sweepLength - endSegment * 90) / 90;
892 int delta = sweepLength > 0 ? 1 : -1;
899 if (qFuzzyIsNull(startT - qreal(1))) {
901 startSegment += delta;
905 if (qFuzzyIsNull(endT)) {
910 startT = qt_t_for_arc_angle(startT * 90);
911 endT = qt_t_for_arc_angle(endT * 90);
913 const bool splitAtStart = !qFuzzyIsNull(startT);
914 const bool splitAtEnd = !qFuzzyIsNull(endT - qreal(1));
916 const int end = endSegment + delta;
919 if (startSegment == end) {
920 const int quadrant = 3 - ((startSegment % 4) + 4) % 4;
921 const int j = 3 * quadrant;
922 return delta > 0 ? points[j + 3] : points[j];
925 QPointF startPoint, endPoint;
926 qt_find_ellipse_coords(rect, startAngle, sweepLength, &startPoint, &endPoint);
928 for (
int i = startSegment; i != end; i += delta) {
929 const int quadrant = 3 - ((i % 4) + 4) % 4;
930 const int j = 3 * quadrant;
934 b = QBezier::fromPoints(points[j + 3], points[j + 2], points[j + 1], points[j]);
936 b = QBezier::fromPoints(points[j], points[j + 1], points[j + 2], points[j + 3]);
939 if (startSegment == endSegment && qFuzzyCompare(startT, endT))
942 if (i == startSegment) {
943 if (i == endSegment && splitAtEnd)
944 b = b.bezierOnInterval(startT, endT);
945 else if (splitAtStart)
946 b = b.bezierOnInterval(startT, 1);
947 }
else if (i == endSegment && splitAtEnd) {
948 b = b.bezierOnInterval(0, endT);
952 curves[(*point_count)++] = b.pt2();
953 curves[(*point_count)++] = b.pt3();
954 curves[(*point_count)++] = b.pt4();
957 Q_ASSERT(*point_count > 0);
958 curves[*(point_count)-1] = endPoint;
1065void QDashStroker::processCurrentSubpath()
1067 int dashCount = qMin(m_dashPattern.size(), 32);
1071 m_customData = m_stroker;
1072 m_stroke_width = m_stroker->strokeWidth();
1073 m_miter_limit = m_stroker->miterLimit();
1076 qreal longestLength = 0;
1077 qreal sumLength = 0;
1078 for (
int i=0; i<dashCount; ++i) {
1079 dashes[i] = qMax(m_dashPattern.at(i), qreal(0)) * m_stroke_width;
1080 sumLength += dashes[i];
1081 if (dashes[i] > longestLength)
1082 longestLength = dashes[i];
1085 if (qFuzzyIsNull(sumLength))
1088 qreal invSumLength = qreal(1) / sumLength;
1090 Q_ASSERT(dashCount > 0);
1092 dashCount = dashCount & -2;
1097 qreal doffset = m_dashOffset * m_stroke_width;
1100 doffset = std::fmod(doffset, sumLength);
1102 doffset += sumLength;
1104 while (doffset >= dashes[idash]) {
1105 doffset -= dashes[idash];
1106 if (++idash >= dashCount)
1115 QSubpathFlatIterator it(&m_elements, m_dashThreshold);
1116 qfixed2d prev = it.next();
1117 if (!prev.isFinite())
1120 bool clipping = !m_clip_rect.isEmpty();
1121 qfixed2d move_to_pos = prev;
1122 qfixed2d line_to_pos;
1125 qfixed padding = qt_real_to_fixed(qMax(m_stroke_width, m_miter_limit) * longestLength);
1126 qfixed2d clip_tl = { qt_real_to_fixed(m_clip_rect.left()) - padding,
1127 qt_real_to_fixed(m_clip_rect.top()) - padding };
1128 qfixed2d clip_br = { qt_real_to_fixed(m_clip_rect.right()) + padding ,
1129 qt_real_to_fixed(m_clip_rect.bottom()) + padding };
1131 bool hasMoveTo =
false;
1132 while (it.hasNext()) {
1133 QStrokerOps::Element e = it.next();
1134 if (!qfixed2d(e).isFinite())
1137 Q_ASSERT(e.isLineTo());
1138 cline = QLineF(qt_fixed_to_real(prev.x),
1139 qt_fixed_to_real(prev.y),
1140 qt_fixed_to_real(e.x),
1141 qt_fixed_to_real(e.y));
1142 elen = cline.length();
1144 estop = estart + elen;
1146 bool done = pos >= estop;
1149 bool clipIt = clipping && !lineIntersectsRect(prev, e, clip_tl, clip_br);
1150 bool skipDashing = elen * invSumLength > repetitionLimit();
1151 int maxDashes = dashCount;
1152 if (skipDashing || clipIt) {
1154 elen -= std::floor(elen * invSumLength) * sumLength;
1157 qreal dpos = pos + dashes[idash] - doffset - estart;
1159 Q_ASSERT(dpos >= 0);
1162 doffset = dashes[idash] - (dpos - elen);
1166 pos = --maxDashes > 0 ? dpos + estart : estop;
1167 done = pos >= estop;
1168 if (++idash >= dashCount)
1178 emitMoveTo(move_to_pos.x, move_to_pos.y);
1181 emitLineTo(e.x, e.y);
1190 bool has_offset = doffset > 0;
1191 bool evenDash = (idash & 1) == 0;
1192 qreal dpos = pos + dashes[idash] - doffset - estart;
1194 Q_ASSERT(dpos >= 0);
1197 doffset = dashes[idash] - (dpos - elen);
1202 p2 = cline.pointAt(dpos/elen);
1203 pos = dpos + estart;
1204 done = pos >= estop;
1205 if (++idash >= dashCount)
1211 line_to_pos.x = qt_real_to_fixed(p2.x());
1212 line_to_pos.y = qt_real_to_fixed(p2.y());
1215 || lineRectIntersectsRect(move_to_pos, line_to_pos, clip_tl, clip_br))
1221 if (!has_offset || !hasMoveTo) {
1222 emitMoveTo(move_to_pos.x, move_to_pos.y);
1226 emitLineTo(line_to_pos.x, line_to_pos.y);
1230 move_to_pos = line_to_pos;
1232 move_to_pos.x = qt_real_to_fixed(p2.x());
1233 move_to_pos.y = qt_real_to_fixed(p2.y());