205void QStrokerOps::strokePath(
const QPainterPath &path,
void *customData,
const QTransform &matrix)
210 setCurveThresholdFromTransform(QTransform());
212 int count = path.elementCount();
213 if (matrix.isIdentity()) {
214 for (
int i=0; i<count; ++i) {
215 const QPainterPath::Element &e = path.elementAt(i);
217 case QPainterPath::MoveToElement:
218 moveTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y));
220 case QPainterPath::LineToElement:
221 lineTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y));
223 case QPainterPath::CurveToElement:
225 const QPainterPath::Element &cp2 = path.elementAt(++i);
226 const QPainterPath::Element &ep = path.elementAt(++i);
227 cubicTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y),
228 qt_real_to_fixed(cp2.x), qt_real_to_fixed(cp2.y),
229 qt_real_to_fixed(ep.x), qt_real_to_fixed(ep.y));
237 for (
int i=0; i<count; ++i) {
238 const QPainterPath::Element &e = path.elementAt(i);
239 QPointF pt = QPointF(e.x, e.y) * matrix;
241 case QPainterPath::MoveToElement:
242 moveTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
244 case QPainterPath::LineToElement:
245 lineTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
247 case QPainterPath::CurveToElement:
249 QPointF cp2 = ((QPointF) path.elementAt(++i)) * matrix;
250 QPointF ep = ((QPointF) path.elementAt(++i)) * matrix;
251 cubicTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()),
252 qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
253 qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
407void QStroker::joinPoints(qfixed focal_x, qfixed focal_y,
const QLineF &nextLine, LineJoinMode join)
409#ifdef QPP_STROKE_DEBUG
410 printf(
" -----> joinPoints: around=(%.0f, %.0f), next_p1=(%.0f, %.f) next_p2=(%.0f, %.f)\n",
411 qt_fixed_to_real(focal_x),
412 qt_fixed_to_real(focal_y),
413 nextLine.x1(), nextLine.y1(), nextLine.x2(), nextLine.y2());
417#if !defined (QFIXED_26_6) && !defined (Q_FIXED_32_32)
418 if (qFuzzyCompare(m_back1X, nextLine.x1()) && qFuzzyCompare(m_back1Y, nextLine.y1()))
421 if (m_back1X == qt_real_to_fixed(nextLine.x1())
422 && m_back1Y == qt_real_to_fixed(nextLine.y1())) {
426 QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y),
427 qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y));
429 QLineF::IntersectionType type = prevLine.intersects(nextLine, &isect);
431 if (join == FlatJoin) {
432 QLineF shortCut(prevLine.p2(), nextLine.p1());
433 qreal angle = shortCut.angleTo(prevLine);
434 if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
435 emitLineTo(focal_x, focal_y);
436 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
439 emitLineTo(qt_real_to_fixed(nextLine.x1()),
440 qt_real_to_fixed(nextLine.y1()));
443 if (join == MiterJoin) {
444 qreal appliedMiterLimit = qt_fixed_to_real(m_strokeWidth * m_miterLimit);
447 QLineF shortCut(prevLine.p2(), nextLine.p1());
448 qreal angle = shortCut.angleTo(prevLine);
449 if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
450 emitLineTo(focal_x, focal_y);
451 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
454 QLineF miterLine(QPointF(qt_fixed_to_real(m_back1X),
455 qt_fixed_to_real(m_back1Y)), isect);
456 if (type == QLineF::NoIntersection || miterLine.length() > appliedMiterLimit) {
458 l1.setLength(appliedMiterLimit);
459 l1.translate(prevLine.dx(), prevLine.dy());
462 l2.setLength(appliedMiterLimit);
463 l2.translate(-l2.dx(), -l2.dy());
465 emitLineTo(qt_real_to_fixed(l1.x2()), qt_real_to_fixed(l1.y2()));
466 emitLineTo(qt_real_to_fixed(l2.x1()), qt_real_to_fixed(l2.y1()));
467 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
469 emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
470 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
473 }
else if (join == SquareJoin) {
474 qfixed offset = m_strokeWidth / 2;
477 qreal dp = QPointF::dotProduct(QPointF(prevLine.dx(), prevLine.dy()), QPointF(nextLine.dx(), nextLine.dy()));
479 l1 = QLineF(prevLine.p2(), prevLine.p1());
481 l1.translate(l1.dx(), l1.dy());
482 l1.setLength(qt_fixed_to_real(offset));
483 QLineF l2(nextLine.p2(), nextLine.p1());
484 l2.translate(l2.dx(), l2.dy());
485 l2.setLength(qt_fixed_to_real(offset));
486 emitLineTo(qt_real_to_fixed(l1.x2()), qt_real_to_fixed(l1.y2()));
487 emitLineTo(qt_real_to_fixed(l2.x2()), qt_real_to_fixed(l2.y2()));
488 emitLineTo(qt_real_to_fixed(l2.x1()), qt_real_to_fixed(l2.y1()));
490 }
else if (join == RoundJoin) {
491 qfixed offset = m_strokeWidth / 2;
493 QLineF shortCut(prevLine.p2(), nextLine.p1());
494 qreal angle = shortCut.angleTo(prevLine);
495 if ((type == QLineF::BoundedIntersection || (angle > qreal(90.01))) && nextLine.length() > offset) {
496 emitLineTo(focal_x, focal_y);
497 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
500 qreal l1_on_x = adapted_angle_on_x(prevLine);
501 qreal l2_on_x = adapted_angle_on_x(nextLine);
503 qreal sweepLength = qAbs(l2_on_x - l1_on_x);
508 QPointF curve_start =
509 qt_curves_for_arc(QRectF(qt_fixed_to_real(focal_x - offset),
510 qt_fixed_to_real(focal_y - offset),
511 qt_fixed_to_real(offset * 2),
512 qt_fixed_to_real(offset * 2)),
513 l1_on_x + 90, -sweepLength,
514 curves, &point_count);
518 Q_UNUSED(curve_start);
520 for (
int i=0; i<point_count; i+=3) {
521 emitCubicTo(qt_real_to_fixed(curves[i].x()),
522 qt_real_to_fixed(curves[i].y()),
523 qt_real_to_fixed(curves[i+1].x()),
524 qt_real_to_fixed(curves[i+1].y()),
525 qt_real_to_fixed(curves[i+2].x()),
526 qt_real_to_fixed(curves[i+2].y()));
530 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
534 }
else if (join == RoundCap) {
535 qfixed offset = m_strokeWidth / 2;
538 QLineF l1 = prevLine;
539 qreal dp = QPointF::dotProduct(QPointF(prevLine.dx(), prevLine.dy()), QPointF(nextLine.dx(), nextLine.dy()));
541 l1 = QLineF(prevLine.p2(), prevLine.p1());
543 l1.translate(l1.dx(), l1.dy());
544 l1.setLength(QT_PATH_KAPPA * offset);
547 QLineF l2(qt_fixed_to_real(focal_x), qt_fixed_to_real(focal_y),
548 prevLine.x2(), prevLine.y2());
549 l2.translate(-l2.dy(), l2.dx());
550 l2.setLength(QT_PATH_KAPPA * offset);
552 emitCubicTo(qt_real_to_fixed(l1.x2()),
553 qt_real_to_fixed(l1.y2()),
554 qt_real_to_fixed(l2.x2()),
555 qt_real_to_fixed(l2.y2()),
556 qt_real_to_fixed(l2.x1()),
557 qt_real_to_fixed(l2.y1()));
560 l2 = QLineF(l2.x1(), l2.y1(), l2.x1()-l2.dx(), l2.y1()-l2.dy());
563 l1.translate(nextLine.x1() - l1.x1(), nextLine.y1() - l1.y1());
565 emitCubicTo(qt_real_to_fixed(l2.x2()),
566 qt_real_to_fixed(l2.y2()),
567 qt_real_to_fixed(l1.x2()),
568 qt_real_to_fixed(l1.y2()),
569 qt_real_to_fixed(l1.x1()),
570 qt_real_to_fixed(l1.y1()));
571 }
else if (join == SvgMiterJoin) {
572 QLineF shortCut(prevLine.p2(), nextLine.p1());
573 qreal angle = shortCut.angleTo(prevLine);
574 if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
575 emitLineTo(focal_x, focal_y);
576 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
579 QLineF miterLine(QPointF(qt_fixed_to_real(focal_x),
580 qt_fixed_to_real(focal_y)), isect);
581 if (type == QLineF::NoIntersection || miterLine.length() > qt_fixed_to_real(m_strokeWidth * m_miterLimit) / 2) {
582 emitLineTo(qt_real_to_fixed(nextLine.x1()),
583 qt_real_to_fixed(nextLine.y1()));
585 emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
586 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
589 Q_ASSERT(!
"QStroker::joinPoints(), bad join style...");
605 QLineF *startTangent)
608 const int MAX_OFFSET = 16;
609 QBezier offsetCurves[MAX_OFFSET];
611 Q_ASSERT(it->hasNext());
612 QStrokerOps::Element first_element = it->next();
613 Q_ASSERT(first_element.isMoveTo());
615 qfixed2d start = first_element;
617#ifdef QPP_STROKE_DEBUG
618 qDebug(
" -> (side) [%.2f, %.2f], startPos=%d",
619 qt_fixed_to_real(start.x),
620 qt_fixed_to_real(start.y));
623 qfixed2d prev = start;
627 qfixed offset = stroker->strokeWidth() / 2;
629 while (it->hasNext()) {
630 QStrokerOps::Element e = it->next();
634#ifdef QPP_STROKE_DEBUG
635 qDebug(
"\n ---> (side) lineto [%.2f, %.2f]", e.x, e.y);
637 QLineF line(qt_fixed_to_real(prev.x), qt_fixed_to_real(prev.y),
638 qt_fixed_to_real(e.x), qt_fixed_to_real(e.y));
639 if (line.p1() != line.p2()) {
640 QLineF normal = line.normalVector();
641 normal.setLength(offset);
642 line.translate(normal.dx(), normal.dy());
647 stroker->joinPoints(prev.x, prev.y, line, stroker->capStyleMode());
649 stroker->emitMoveTo(qt_real_to_fixed(line.x1()), qt_real_to_fixed(line.y1()));
650 *startTangent = line;
653 stroker->joinPoints(prev.x, prev.y, line, stroker->joinStyleMode());
657 stroker->emitLineTo(qt_real_to_fixed(line.x2()),
658 qt_real_to_fixed(line.y2()));
663 }
else if (e.isCurveTo()) {
664 QStrokerOps::Element cp2 = it->next();
665 QStrokerOps::Element ep = it->next();
667#ifdef QPP_STROKE_DEBUG
668 qDebug(
"\n ---> (side) cubicTo [%.2f, %.2f]",
669 qt_fixed_to_real(ep.x),
670 qt_fixed_to_real(ep.y));
674 QBezier::fromPoints(QPointF(qt_fixed_to_real(prev.x), qt_fixed_to_real(prev.y)),
675 QPointF(qt_fixed_to_real(e.x), qt_fixed_to_real(e.y)),
676 QPointF(qt_fixed_to_real(cp2.x), qt_fixed_to_real(cp2.y)),
677 QPointF(qt_fixed_to_real(ep.x), qt_fixed_to_real(ep.y)));
678 int count = bezier.shifted(offsetCurves,
681 stroker->curveThreshold());
685 QLineF tangent = bezier.startTangent();
686 tangent.translate(offsetCurves[0].pt1() - bezier.pt1());
688 QPointF pt = offsetCurves[0].pt1();
690 stroker->joinPoints(prev.x, prev.y,
692 stroker->capStyleMode());
694 stroker->emitMoveTo(qt_real_to_fixed(pt.x()),
695 qt_real_to_fixed(pt.y()));
697 *startTangent = tangent;
700 stroker->joinPoints(prev.x, prev.y,
702 stroker->joinStyleMode());
706 for (
int i=0; i<count; ++i) {
707 QPointF cp1 = offsetCurves[i].pt2();
708 QPointF cp2 = offsetCurves[i].pt3();
709 QPointF ep = offsetCurves[i].pt4();
710 stroker->emitCubicTo(qt_real_to_fixed(cp1.x()), qt_real_to_fixed(cp1.y()),
711 qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
712 qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
720 if (start == prev && !stroker->forceOpen()) {
722#ifdef QPP_STROKE_DEBUG
723 qDebug(
"\n ---> (side) closed subpath");
727 stroker->joinPoints(prev.x, prev.y, *startTangent, stroker->joinStyleMode());
730#ifdef QPP_STROKE_DEBUG
731 qDebug(
"\n ---> (side) open subpath");
823 QPointF *curves,
int *point_count)
825 Q_ASSERT(point_count);
829 if (qt_is_nan(rect.x()) || qt_is_nan(rect.y()) || qt_is_nan(rect.width()) || qt_is_nan(rect.height())
830 || qt_is_nan(startAngle) || qt_is_nan(sweepLength)) {
831 qWarning(
"QPainterPath::arcTo: Adding arc where a parameter is NaN, results are undefined");
842 qreal w = rect.width();
843 qreal w2 = rect.width() / 2;
844 qreal w2k = w2 * QT_PATH_KAPPA;
846 qreal h = rect.height();
847 qreal h2 = rect.height() / 2;
848 qreal h2k = h2 * QT_PATH_KAPPA;
853 QPointF(x + w, y + h2),
856 QPointF(x + w, y + h2 + h2k),
857 QPointF(x + w2 + w2k, y + h),
858 QPointF(x + w2, y + h),
861 QPointF(x + w2 - w2k, y + h),
862 QPointF(x, y + h2 + h2k),
866 QPointF(x, y + h2 - h2k),
867 QPointF(x + w2 - w2k, y),
871 QPointF(x + w2 + w2k, y),
872 QPointF(x + w, y + h2 - h2k),
873 QPointF(x + w, y + h2)
876 if (sweepLength > 360) sweepLength = 360;
877 else if (sweepLength < -360) sweepLength = -360;
880 if (startAngle == 0.0) {
881 if (sweepLength == 360.0) {
882 for (
int i = 11; i >= 0; --i)
883 curves[(*point_count)++] = points[i];
885 }
else if (sweepLength == -360.0) {
886 for (
int i = 1; i <= 12; ++i)
887 curves[(*point_count)++] = points[i];
892 int startSegment =
int(qFloor(startAngle / 90));
893 int endSegment =
int(qFloor((startAngle + sweepLength) / 90));
895 qreal startT = (startAngle - startSegment * 90) / 90;
896 qreal endT = (startAngle + sweepLength - endSegment * 90) / 90;
898 int delta = sweepLength > 0 ? 1 : -1;
905 if (qFuzzyIsNull(startT - qreal(1))) {
907 startSegment += delta;
911 if (qFuzzyIsNull(endT)) {
916 startT = qt_t_for_arc_angle(startT * 90);
917 endT = qt_t_for_arc_angle(endT * 90);
919 const bool splitAtStart = !qFuzzyIsNull(startT);
920 const bool splitAtEnd = !qFuzzyIsNull(endT - qreal(1));
922 const int end = endSegment + delta;
925 if (startSegment == end) {
926 const int quadrant = 3 - ((startSegment % 4) + 4) % 4;
927 const int j = 3 * quadrant;
928 return delta > 0 ? points[j + 3] : points[j];
931 QPointF startPoint, endPoint;
932 qt_find_ellipse_coords(rect, startAngle, sweepLength, &startPoint, &endPoint);
934 for (
int i = startSegment; i != end; i += delta) {
935 const int quadrant = 3 - ((i % 4) + 4) % 4;
936 const int j = 3 * quadrant;
940 b = QBezier::fromPoints(points[j + 3], points[j + 2], points[j + 1], points[j]);
942 b = QBezier::fromPoints(points[j], points[j + 1], points[j + 2], points[j + 3]);
945 if (startSegment == endSegment && qFuzzyCompare(startT, endT))
948 if (i == startSegment) {
949 if (i == endSegment && splitAtEnd)
950 b = b.bezierOnInterval(startT, endT);
951 else if (splitAtStart)
952 b = b.bezierOnInterval(startT, 1);
953 }
else if (i == endSegment && splitAtEnd) {
954 b = b.bezierOnInterval(0, endT);
958 curves[(*point_count)++] = b.pt2();
959 curves[(*point_count)++] = b.pt3();
960 curves[(*point_count)++] = b.pt4();
963 Q_ASSERT(*point_count > 0);
964 curves[*(point_count)-1] = endPoint;
1071void QDashStroker::processCurrentSubpath()
1073 int dashCount = qMin(m_dashPattern.size(), 32);
1077 m_customData = m_stroker;
1078 m_stroke_width = m_stroker->strokeWidth();
1079 m_miter_limit = m_stroker->miterLimit();
1082 qreal longestLength = 0;
1083 qreal sumLength = 0;
1084 for (
int i=0; i<dashCount; ++i) {
1085 dashes[i] = qMax(m_dashPattern.at(i), qreal(0)) * m_stroke_width;
1086 sumLength += dashes[i];
1087 if (dashes[i] > longestLength)
1088 longestLength = dashes[i];
1091 if (qFuzzyIsNull(sumLength))
1094 qreal invSumLength = qreal(1) / sumLength;
1096 Q_ASSERT(dashCount > 0);
1098 dashCount = dashCount & -2;
1103 qreal doffset = m_dashOffset * m_stroke_width;
1106 doffset = std::fmod(doffset, sumLength);
1108 doffset += sumLength;
1110 while (doffset >= dashes[idash]) {
1111 doffset -= dashes[idash];
1112 if (++idash >= dashCount)
1121 QSubpathFlatIterator it(&m_elements, m_dashThreshold);
1122 qfixed2d prev = it.next();
1123 if (!prev.isFinite())
1126 bool clipping = !m_clip_rect.isEmpty();
1127 qfixed2d move_to_pos = prev;
1128 qfixed2d line_to_pos;
1131 qfixed padding = qt_real_to_fixed(qMax(m_stroke_width, m_miter_limit) * longestLength);
1132 qfixed2d clip_tl = { qt_real_to_fixed(m_clip_rect.left()) - padding,
1133 qt_real_to_fixed(m_clip_rect.top()) - padding };
1134 qfixed2d clip_br = { qt_real_to_fixed(m_clip_rect.right()) + padding ,
1135 qt_real_to_fixed(m_clip_rect.bottom()) + padding };
1137 bool hasMoveTo =
false;
1138 while (it.hasNext()) {
1139 QStrokerOps::Element e = it.next();
1140 if (!qfixed2d(e).isFinite())
1143 Q_ASSERT(e.isLineTo());
1144 cline = QLineF(qt_fixed_to_real(prev.x),
1145 qt_fixed_to_real(prev.y),
1146 qt_fixed_to_real(e.x),
1147 qt_fixed_to_real(e.y));
1148 elen = cline.length();
1150 estop = estart + elen;
1152 bool done = pos >= estop;
1155 bool clipIt = clipping && !lineIntersectsRect(prev, e, clip_tl, clip_br);
1156 bool skipDashing = elen * invSumLength > repetitionLimit();
1157 int maxDashes = dashCount;
1158 if (skipDashing || clipIt) {
1160 elen -= std::floor(elen * invSumLength) * sumLength;
1164 qreal dpos = (pos + dashes[idash]) - (doffset + estart);
1166 Q_ASSERT(dpos >= 0);
1169 doffset = dashes[idash] - (dpos - elen);
1173 pos = --maxDashes > 0 ? dpos + estart : estop;
1174 done = pos >= estop;
1175 if (++idash >= dashCount)
1185 emitMoveTo(move_to_pos.x, move_to_pos.y);
1188 emitLineTo(e.x, e.y);
1197 bool has_offset = doffset > 0;
1198 bool evenDash = (idash & 1) == 0;
1200 qreal dpos = (pos + dashes[idash]) - (doffset + estart);
1202 Q_ASSERT(dpos >= 0);
1205 doffset = dashes[idash] - (dpos - elen);
1210 p2 = cline.pointAt(dpos/elen);
1211 pos = dpos + estart;
1212 done = pos >= estop;
1213 if (++idash >= dashCount)
1219 line_to_pos.x = qt_real_to_fixed(p2.x());
1220 line_to_pos.y = qt_real_to_fixed(p2.y());
1223 || lineRectIntersectsRect(move_to_pos, line_to_pos, clip_tl, clip_br))
1229 if (!has_offset || !hasMoveTo) {
1230 emitMoveTo(move_to_pos.x, move_to_pos.y);
1234 emitLineTo(line_to_pos.x, line_to_pos.y);
1238 move_to_pos = line_to_pos;
1240 move_to_pos.x = qt_real_to_fixed(p2.x());
1241 move_to_pos.y = qt_real_to_fixed(p2.y());