206void QStrokerOps::strokePath(
const QPainterPath &path,
void *customData,
const QTransform &matrix)
211 setCurveThresholdFromTransform(QTransform());
213 int count = path.elementCount();
214 if (matrix.isIdentity()) {
215 for (
int i=0; i<count; ++i) {
216 const QPainterPath::Element &e = path.elementAt(i);
218 case QPainterPath::MoveToElement:
219 moveTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y));
221 case QPainterPath::LineToElement:
222 lineTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y));
224 case QPainterPath::CurveToElement:
226 const QPainterPath::Element &cp2 = path.elementAt(++i);
227 const QPainterPath::Element &ep = path.elementAt(++i);
228 cubicTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y),
229 qt_real_to_fixed(cp2.x), qt_real_to_fixed(cp2.y),
230 qt_real_to_fixed(ep.x), qt_real_to_fixed(ep.y));
238 for (
int i=0; i<count; ++i) {
239 const QPainterPath::Element &e = path.elementAt(i);
240 QPointF pt = QPointF(e.x, e.y) * matrix;
242 case QPainterPath::MoveToElement:
243 moveTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
245 case QPainterPath::LineToElement:
246 lineTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
248 case QPainterPath::CurveToElement:
250 QPointF cp2 = ((QPointF) path.elementAt(++i)) * matrix;
251 QPointF ep = ((QPointF) path.elementAt(++i)) * matrix;
252 cubicTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()),
253 qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
254 qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
408void QStroker::joinPoints(qfixed focal_x, qfixed focal_y,
const QLineF &nextLine, LineJoinMode join)
410#ifdef QPP_STROKE_DEBUG
411 printf(
" -----> joinPoints: around=(%.0f, %.0f), next_p1=(%.0f, %.f) next_p2=(%.0f, %.f)\n",
412 qt_fixed_to_real(focal_x),
413 qt_fixed_to_real(focal_y),
414 nextLine.x1(), nextLine.y1(), nextLine.x2(), nextLine.y2());
418#if !defined (QFIXED_26_6) && !defined (Q_FIXED_32_32)
419 if (qFuzzyCompare(m_back1X, nextLine.x1()) && qFuzzyCompare(m_back1Y, nextLine.y1()))
422 if (m_back1X == qt_real_to_fixed(nextLine.x1())
423 && m_back1Y == qt_real_to_fixed(nextLine.y1())) {
427 QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y),
428 qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y));
430 QLineF::IntersectionType type = prevLine.intersects(nextLine, &isect);
432 if (join == FlatJoin) {
433 QLineF shortCut(prevLine.p2(), nextLine.p1());
434 qreal angle = shortCut.angleTo(prevLine);
435 if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
436 emitLineTo(focal_x, focal_y);
437 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
440 emitLineTo(qt_real_to_fixed(nextLine.x1()),
441 qt_real_to_fixed(nextLine.y1()));
444 if (join == MiterJoin) {
445 qreal appliedMiterLimit = qt_fixed_to_real(m_strokeWidth * m_miterLimit);
448 QLineF shortCut(prevLine.p2(), nextLine.p1());
449 qreal angle = shortCut.angleTo(prevLine);
450 if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
451 emitLineTo(focal_x, focal_y);
452 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
455 QLineF miterLine(QPointF(qt_fixed_to_real(m_back1X),
456 qt_fixed_to_real(m_back1Y)), isect);
457 if (type == QLineF::NoIntersection || miterLine.length() > appliedMiterLimit) {
459 l1.setLength(appliedMiterLimit);
460 l1.translate(prevLine.dx(), prevLine.dy());
463 l2.setLength(appliedMiterLimit);
464 l2.translate(-l2.dx(), -l2.dy());
466 emitLineTo(qt_real_to_fixed(l1.x2()), qt_real_to_fixed(l1.y2()));
467 emitLineTo(qt_real_to_fixed(l2.x1()), qt_real_to_fixed(l2.y1()));
468 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
470 emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
471 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
474 }
else if (join == SquareJoin) {
475 qfixed offset = m_strokeWidth / 2;
478 qreal dp = QPointF::dotProduct(QPointF(prevLine.dx(), prevLine.dy()), QPointF(nextLine.dx(), nextLine.dy()));
480 l1 = QLineF(prevLine.p2(), prevLine.p1());
482 l1.translate(l1.dx(), l1.dy());
483 l1.setLength(qt_fixed_to_real(offset));
484 QLineF l2(nextLine.p2(), nextLine.p1());
485 l2.translate(l2.dx(), l2.dy());
486 l2.setLength(qt_fixed_to_real(offset));
487 emitLineTo(qt_real_to_fixed(l1.x2()), qt_real_to_fixed(l1.y2()));
488 emitLineTo(qt_real_to_fixed(l2.x2()), qt_real_to_fixed(l2.y2()));
489 emitLineTo(qt_real_to_fixed(l2.x1()), qt_real_to_fixed(l2.y1()));
491 }
else if (join == RoundJoin) {
492 qfixed offset = m_strokeWidth / 2;
494 QLineF shortCut(prevLine.p2(), nextLine.p1());
495 qreal angle = shortCut.angleTo(prevLine);
496 if ((type == QLineF::BoundedIntersection || (angle > qreal(90.01))) && nextLine.length() > offset) {
497 emitLineTo(focal_x, focal_y);
498 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
501 qreal l1_on_x = adapted_angle_on_x(prevLine);
502 qreal l2_on_x = adapted_angle_on_x(nextLine);
504 qreal sweepLength = qAbs(l2_on_x - l1_on_x);
509 QPointF curve_start =
510 qt_curves_for_arc(QRectF(qt_fixed_to_real(focal_x - offset),
511 qt_fixed_to_real(focal_y - offset),
512 qt_fixed_to_real(offset * 2),
513 qt_fixed_to_real(offset * 2)),
514 l1_on_x + 90, -sweepLength,
515 curves, &point_count);
519 Q_UNUSED(curve_start);
521 for (
int i=0; i<point_count; i+=3) {
522 emitCubicTo(qt_real_to_fixed(curves[i].x()),
523 qt_real_to_fixed(curves[i].y()),
524 qt_real_to_fixed(curves[i+1].x()),
525 qt_real_to_fixed(curves[i+1].y()),
526 qt_real_to_fixed(curves[i+2].x()),
527 qt_real_to_fixed(curves[i+2].y()));
531 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
535 }
else if (join == RoundCap) {
536 qfixed offset = m_strokeWidth / 2;
539 QLineF l1 = prevLine;
540 qreal dp = QPointF::dotProduct(QPointF(prevLine.dx(), prevLine.dy()), QPointF(nextLine.dx(), nextLine.dy()));
542 l1 = QLineF(prevLine.p2(), prevLine.p1());
544 l1.translate(l1.dx(), l1.dy());
545 l1.setLength(QT_PATH_KAPPA * offset);
548 QLineF l2(qt_fixed_to_real(focal_x), qt_fixed_to_real(focal_y),
549 prevLine.x2(), prevLine.y2());
550 l2.translate(-l2.dy(), l2.dx());
551 l2.setLength(QT_PATH_KAPPA * offset);
553 emitCubicTo(qt_real_to_fixed(l1.x2()),
554 qt_real_to_fixed(l1.y2()),
555 qt_real_to_fixed(l2.x2()),
556 qt_real_to_fixed(l2.y2()),
557 qt_real_to_fixed(l2.x1()),
558 qt_real_to_fixed(l2.y1()));
561 l2 = QLineF(l2.x1(), l2.y1(), l2.x1()-l2.dx(), l2.y1()-l2.dy());
564 l1.translate(nextLine.x1() - l1.x1(), nextLine.y1() - l1.y1());
566 emitCubicTo(qt_real_to_fixed(l2.x2()),
567 qt_real_to_fixed(l2.y2()),
568 qt_real_to_fixed(l1.x2()),
569 qt_real_to_fixed(l1.y2()),
570 qt_real_to_fixed(l1.x1()),
571 qt_real_to_fixed(l1.y1()));
572 }
else if (join == SvgMiterJoin) {
573 QLineF shortCut(prevLine.p2(), nextLine.p1());
574 qreal angle = shortCut.angleTo(prevLine);
575 if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
576 emitLineTo(focal_x, focal_y);
577 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
580 QLineF miterLine(QPointF(qt_fixed_to_real(focal_x),
581 qt_fixed_to_real(focal_y)), isect);
582 if (type == QLineF::NoIntersection || miterLine.length() > qt_fixed_to_real(m_strokeWidth * m_miterLimit) / 2) {
583 emitLineTo(qt_real_to_fixed(nextLine.x1()),
584 qt_real_to_fixed(nextLine.y1()));
586 emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
587 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
590 Q_ASSERT(!
"QStroker::joinPoints(), bad join style...");
606 QLineF *startTangent)
609 const int MAX_OFFSET = 16;
610 QBezier offsetCurves[MAX_OFFSET];
612 Q_ASSERT(it->hasNext());
613 QStrokerOps::Element first_element = it->next();
614 Q_ASSERT(first_element.isMoveTo());
616 qfixed2d start = first_element;
618#ifdef QPP_STROKE_DEBUG
619 qDebug(
" -> (side) [%.2f, %.2f], startPos=%d",
620 qt_fixed_to_real(start.x),
621 qt_fixed_to_real(start.y));
624 qfixed2d prev = start;
628 qfixed offset = stroker->strokeWidth() / 2;
630 while (it->hasNext()) {
631 QStrokerOps::Element e = it->next();
635#ifdef QPP_STROKE_DEBUG
636 qDebug(
"\n ---> (side) lineto [%.2f, %.2f]", e.x, e.y);
638 QLineF line(qt_fixed_to_real(prev.x), qt_fixed_to_real(prev.y),
639 qt_fixed_to_real(e.x), qt_fixed_to_real(e.y));
640 if (line.p1() != line.p2()) {
641 QLineF normal = line.normalVector();
642 normal.setLength(offset);
643 line.translate(normal.dx(), normal.dy());
648 stroker->joinPoints(prev.x, prev.y, line, stroker->capStyleMode());
650 stroker->emitMoveTo(qt_real_to_fixed(line.x1()), qt_real_to_fixed(line.y1()));
651 *startTangent = line;
654 stroker->joinPoints(prev.x, prev.y, line, stroker->joinStyleMode());
658 stroker->emitLineTo(qt_real_to_fixed(line.x2()),
659 qt_real_to_fixed(line.y2()));
664 }
else if (e.isCurveTo()) {
665 QStrokerOps::Element cp2 = it->next();
666 QStrokerOps::Element ep = it->next();
668#ifdef QPP_STROKE_DEBUG
669 qDebug(
"\n ---> (side) cubicTo [%.2f, %.2f]",
670 qt_fixed_to_real(ep.x),
671 qt_fixed_to_real(ep.y));
675 QBezier::fromPoints(QPointF(qt_fixed_to_real(prev.x), qt_fixed_to_real(prev.y)),
676 QPointF(qt_fixed_to_real(e.x), qt_fixed_to_real(e.y)),
677 QPointF(qt_fixed_to_real(cp2.x), qt_fixed_to_real(cp2.y)),
678 QPointF(qt_fixed_to_real(ep.x), qt_fixed_to_real(ep.y)));
679 int count = bezier.shifted(offsetCurves,
682 stroker->curveThreshold());
686 QLineF tangent = bezier.startTangent();
687 tangent.translate(offsetCurves[0].pt1() - bezier.pt1());
689 QPointF pt = offsetCurves[0].pt1();
691 stroker->joinPoints(prev.x, prev.y,
693 stroker->capStyleMode());
695 stroker->emitMoveTo(qt_real_to_fixed(pt.x()),
696 qt_real_to_fixed(pt.y()));
698 *startTangent = tangent;
701 stroker->joinPoints(prev.x, prev.y,
703 stroker->joinStyleMode());
707 for (
int i=0; i<count; ++i) {
708 QPointF cp1 = offsetCurves[i].pt2();
709 QPointF cp2 = offsetCurves[i].pt3();
710 QPointF ep = offsetCurves[i].pt4();
711 stroker->emitCubicTo(qt_real_to_fixed(cp1.x()), qt_real_to_fixed(cp1.y()),
712 qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
713 qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
721 if (start == prev && !stroker->forceOpen()) {
723#ifdef QPP_STROKE_DEBUG
724 qDebug(
"\n ---> (side) closed subpath");
728 stroker->joinPoints(prev.x, prev.y, *startTangent, stroker->joinStyleMode());
731#ifdef QPP_STROKE_DEBUG
732 qDebug(
"\n ---> (side) open subpath");
824 QPointF *curves,
int *point_count)
826 Q_ASSERT(point_count);
830 if (qt_is_nan(rect.x()) || qt_is_nan(rect.y()) || qt_is_nan(rect.width()) || qt_is_nan(rect.height())
831 || qt_is_nan(startAngle) || qt_is_nan(sweepLength)) {
832 qWarning(
"QPainterPath::arcTo: Adding arc where a parameter is NaN, results are undefined");
843 qreal w = rect.width();
844 qreal w2 = rect.width() / 2;
845 qreal w2k = w2 * QT_PATH_KAPPA;
847 qreal h = rect.height();
848 qreal h2 = rect.height() / 2;
849 qreal h2k = h2 * QT_PATH_KAPPA;
854 QPointF(x + w, y + h2),
857 QPointF(x + w, y + h2 + h2k),
858 QPointF(x + w2 + w2k, y + h),
859 QPointF(x + w2, y + h),
862 QPointF(x + w2 - w2k, y + h),
863 QPointF(x, y + h2 + h2k),
867 QPointF(x, y + h2 - h2k),
868 QPointF(x + w2 - w2k, y),
872 QPointF(x + w2 + w2k, y),
873 QPointF(x + w, y + h2 - h2k),
874 QPointF(x + w, y + h2)
877 if (sweepLength > 360) sweepLength = 360;
878 else if (sweepLength < -360) sweepLength = -360;
881 if (startAngle == 0.0) {
882 if (sweepLength == 360.0) {
883 for (
int i = 11; i >= 0; --i)
884 curves[(*point_count)++] = points[i];
886 }
else if (sweepLength == -360.0) {
887 for (
int i = 1; i <= 12; ++i)
888 curves[(*point_count)++] = points[i];
893 int startSegment =
int(qFloor(startAngle / 90));
894 int endSegment =
int(qFloor((startAngle + sweepLength) / 90));
896 qreal startT = (startAngle - startSegment * 90) / 90;
897 qreal endT = (startAngle + sweepLength - endSegment * 90) / 90;
899 int delta = sweepLength > 0 ? 1 : -1;
906 if (qFuzzyIsNull(startT - qreal(1))) {
908 startSegment += delta;
912 if (qFuzzyIsNull(endT)) {
917 startT = qt_t_for_arc_angle(startT * 90);
918 endT = qt_t_for_arc_angle(endT * 90);
920 const bool splitAtStart = !qFuzzyIsNull(startT);
921 const bool splitAtEnd = !qFuzzyIsNull(endT - qreal(1));
923 const int end = endSegment + delta;
926 if (startSegment == end) {
927 const int quadrant = 3 - ((startSegment % 4) + 4) % 4;
928 const int j = 3 * quadrant;
929 return delta > 0 ? points[j + 3] : points[j];
932 QPointF startPoint, endPoint;
933 qt_find_ellipse_coords(rect, startAngle, sweepLength, &startPoint, &endPoint);
935 for (
int i = startSegment; i != end; i += delta) {
936 const int quadrant = 3 - ((i % 4) + 4) % 4;
937 const int j = 3 * quadrant;
941 b = QBezier::fromPoints(points[j + 3], points[j + 2], points[j + 1], points[j]);
943 b = QBezier::fromPoints(points[j], points[j + 1], points[j + 2], points[j + 3]);
946 if (startSegment == endSegment && qFuzzyCompare(startT, endT))
949 if (i == startSegment) {
950 if (i == endSegment && splitAtEnd)
951 b = b.bezierOnInterval(startT, endT);
952 else if (splitAtStart)
953 b = b.bezierOnInterval(startT, 1);
954 }
else if (i == endSegment && splitAtEnd) {
955 b = b.bezierOnInterval(0, endT);
959 curves[(*point_count)++] = b.pt2();
960 curves[(*point_count)++] = b.pt3();
961 curves[(*point_count)++] = b.pt4();
964 Q_ASSERT(*point_count > 0);
965 curves[*(point_count)-1] = endPoint;
1072void QDashStroker::processCurrentSubpath()
1074 int dashCount = qMin(m_dashPattern.size(), 32);
1078 m_customData = m_stroker;
1079 m_stroke_width = m_stroker->strokeWidth();
1080 m_miter_limit = m_stroker->miterLimit();
1083 qreal longestLength = 0;
1084 qreal sumLength = 0;
1085 for (
int i=0; i<dashCount; ++i) {
1086 dashes[i] = qMax(m_dashPattern.at(i), qreal(0)) * m_stroke_width;
1087 sumLength += dashes[i];
1088 if (dashes[i] > longestLength)
1089 longestLength = dashes[i];
1092 if (qFuzzyIsNull(sumLength))
1095 qreal invSumLength = qreal(1) / sumLength;
1097 Q_ASSERT(dashCount > 0);
1099 dashCount = dashCount & -2;
1104 qreal doffset = m_dashOffset * m_stroke_width;
1107 doffset = std::fmod(doffset, sumLength);
1109 doffset += sumLength;
1111 while (doffset >= dashes[idash]) {
1112 doffset -= dashes[idash];
1113 if (++idash >= dashCount)
1122 QSubpathFlatIterator it(&m_elements, m_dashThreshold);
1123 qfixed2d prev = it.next();
1124 if (!prev.isFinite())
1127 bool clipping = !m_clip_rect.isEmpty();
1128 qfixed2d move_to_pos = prev;
1129 qfixed2d line_to_pos;
1132 qfixed padding = qt_real_to_fixed(qMax(m_stroke_width, m_miter_limit) * longestLength);
1133 qfixed2d clip_tl = { qt_real_to_fixed(m_clip_rect.left()) - padding,
1134 qt_real_to_fixed(m_clip_rect.top()) - padding };
1135 qfixed2d clip_br = { qt_real_to_fixed(m_clip_rect.right()) + padding ,
1136 qt_real_to_fixed(m_clip_rect.bottom()) + padding };
1138 bool hasMoveTo =
false;
1139 while (it.hasNext()) {
1140 QStrokerOps::Element e = it.next();
1141 if (!qfixed2d(e).isFinite())
1144 Q_ASSERT(e.isLineTo());
1145 cline = QLineF(qt_fixed_to_real(prev.x),
1146 qt_fixed_to_real(prev.y),
1147 qt_fixed_to_real(e.x),
1148 qt_fixed_to_real(e.y));
1149 elen = cline.length();
1151 estop = estart + elen;
1153 bool done = pos >= estop;
1156 bool clipIt = clipping && !lineIntersectsRect(prev, e, clip_tl, clip_br);
1157 bool skipDashing = elen * invSumLength > repetitionLimit();
1158 int maxDashes = dashCount;
1159 if (skipDashing || clipIt) {
1161 elen -= std::floor(elen * invSumLength) * sumLength;
1165 qreal dpos = (pos + dashes[idash]) - (doffset + estart);
1167 Q_ASSERT(dpos >= 0);
1170 doffset = dashes[idash] - (dpos - elen);
1174 pos = --maxDashes > 0 ? dpos + estart : estop;
1175 done = pos >= estop;
1176 if (++idash >= dashCount)
1186 emitMoveTo(move_to_pos.x, move_to_pos.y);
1189 emitLineTo(e.x, e.y);
1198 bool has_offset = doffset > 0;
1199 bool evenDash = (idash & 1) == 0;
1201 qreal dpos = (pos + dashes[idash]) - (doffset + estart);
1203 Q_ASSERT(dpos >= 0);
1206 doffset = dashes[idash] - (dpos - elen);
1211 p2 = cline.pointAt(dpos/elen);
1212 pos = dpos + estart;
1213 done = pos >= estop;
1214 if (++idash >= dashCount)
1220 line_to_pos.x = qt_real_to_fixed(p2.x());
1221 line_to_pos.y = qt_real_to_fixed(p2.y());
1224 || lineRectIntersectsRect(move_to_pos, line_to_pos, clip_tl, clip_br))
1230 if (!has_offset || !hasMoveTo) {
1231 emitMoveTo(move_to_pos.x, move_to_pos.y);
1235 emitLineTo(line_to_pos.x, line_to_pos.y);
1239 move_to_pos = line_to_pos;
1241 move_to_pos.x = qt_real_to_fixed(p2.x());
1242 move_to_pos.y = qt_real_to_fixed(p2.y());