207void QStrokerOps::strokePath(
const QPainterPath &path,
void *customData,
const QTransform &matrix)
212 setCurveThresholdFromTransform(QTransform());
214 int count = path.elementCount();
215 if (matrix.isIdentity()) {
216 for (
int i=0; i<count; ++i) {
217 const QPainterPath::Element &e = path.elementAt(i);
219 case QPainterPath::MoveToElement:
220 moveTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y));
222 case QPainterPath::LineToElement:
223 lineTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y));
225 case QPainterPath::CurveToElement:
227 const QPainterPath::Element &cp2 = path.elementAt(++i);
228 const QPainterPath::Element &ep = path.elementAt(++i);
229 cubicTo(qt_real_to_fixed(e.x), qt_real_to_fixed(e.y),
230 qt_real_to_fixed(cp2.x), qt_real_to_fixed(cp2.y),
231 qt_real_to_fixed(ep.x), qt_real_to_fixed(ep.y));
239 for (
int i=0; i<count; ++i) {
240 const QPainterPath::Element &e = path.elementAt(i);
241 QPointF pt = QPointF(e.x, e.y) * matrix;
243 case QPainterPath::MoveToElement:
244 moveTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
246 case QPainterPath::LineToElement:
247 lineTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()));
249 case QPainterPath::CurveToElement:
251 QPointF cp2 = ((QPointF) path.elementAt(++i)) * matrix;
252 QPointF ep = ((QPointF) path.elementAt(++i)) * matrix;
253 cubicTo(qt_real_to_fixed(pt.x()), qt_real_to_fixed(pt.y()),
254 qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
255 qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
409void QStroker::joinPoints(qfixed focal_x, qfixed focal_y,
const QLineF &nextLine, LineJoinMode join)
411#ifdef QPP_STROKE_DEBUG
412 printf(
" -----> joinPoints: around=(%.0f, %.0f), next_p1=(%.0f, %.f) next_p2=(%.0f, %.f)\n",
413 qt_fixed_to_real(focal_x),
414 qt_fixed_to_real(focal_y),
415 nextLine.x1(), nextLine.y1(), nextLine.x2(), nextLine.y2());
419#if !defined (QFIXED_26_6) && !defined (Q_FIXED_32_32)
420 if (qFuzzyCompare(m_back1X, nextLine.x1()) && qFuzzyCompare(m_back1Y, nextLine.y1()))
423 if (m_back1X == qt_real_to_fixed(nextLine.x1())
424 && m_back1Y == qt_real_to_fixed(nextLine.y1())) {
428 QLineF prevLine(qt_fixed_to_real(m_back2X), qt_fixed_to_real(m_back2Y),
429 qt_fixed_to_real(m_back1X), qt_fixed_to_real(m_back1Y));
431 QLineF::IntersectionType type = prevLine.intersects(nextLine, &isect);
433 if (join == FlatJoin) {
434 QLineF shortCut(prevLine.p2(), nextLine.p1());
435 qreal angle = shortCut.angleTo(prevLine);
436 if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
437 emitLineTo(focal_x, focal_y);
438 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
441 emitLineTo(qt_real_to_fixed(nextLine.x1()),
442 qt_real_to_fixed(nextLine.y1()));
445 if (join == MiterJoin) {
446 qreal appliedMiterLimit = qt_fixed_to_real(m_strokeWidth * m_miterLimit);
449 QLineF shortCut(prevLine.p2(), nextLine.p1());
450 qreal angle = shortCut.angleTo(prevLine);
451 if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
452 emitLineTo(focal_x, focal_y);
453 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
456 QLineF miterLine(QPointF(qt_fixed_to_real(m_back1X),
457 qt_fixed_to_real(m_back1Y)), isect);
458 if (type == QLineF::NoIntersection || miterLine.length() > appliedMiterLimit) {
460 l1.setLength(appliedMiterLimit);
461 l1.translate(prevLine.dx(), prevLine.dy());
464 l2.setLength(appliedMiterLimit);
465 l2.translate(-l2.dx(), -l2.dy());
467 emitLineTo(qt_real_to_fixed(l1.x2()), qt_real_to_fixed(l1.y2()));
468 emitLineTo(qt_real_to_fixed(l2.x1()), qt_real_to_fixed(l2.y1()));
469 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
471 emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
472 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
475 }
else if (join == SquareJoin) {
476 qfixed offset = m_strokeWidth / 2;
479 qreal dp = QPointF::dotProduct(QPointF(prevLine.dx(), prevLine.dy()), QPointF(nextLine.dx(), nextLine.dy()));
481 l1 = QLineF(prevLine.p2(), prevLine.p1());
483 l1.translate(l1.dx(), l1.dy());
484 l1.setLength(qt_fixed_to_real(offset));
485 QLineF l2(nextLine.p2(), nextLine.p1());
486 l2.translate(l2.dx(), l2.dy());
487 l2.setLength(qt_fixed_to_real(offset));
488 emitLineTo(qt_real_to_fixed(l1.x2()), qt_real_to_fixed(l1.y2()));
489 emitLineTo(qt_real_to_fixed(l2.x2()), qt_real_to_fixed(l2.y2()));
490 emitLineTo(qt_real_to_fixed(l2.x1()), qt_real_to_fixed(l2.y1()));
492 }
else if (join == RoundJoin) {
493 qfixed offset = m_strokeWidth / 2;
495 QLineF shortCut(prevLine.p2(), nextLine.p1());
496 qreal angle = shortCut.angleTo(prevLine);
497 if ((type == QLineF::BoundedIntersection || (angle > qreal(90.01))) && nextLine.length() > offset) {
498 emitLineTo(focal_x, focal_y);
499 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
502 qreal l1_on_x = adapted_angle_on_x(prevLine);
503 qreal l2_on_x = adapted_angle_on_x(nextLine);
505 qreal sweepLength = qAbs(l2_on_x - l1_on_x);
510 QPointF curve_start =
511 qt_curves_for_arc(QRectF(qt_fixed_to_real(focal_x - offset),
512 qt_fixed_to_real(focal_y - offset),
513 qt_fixed_to_real(offset * 2),
514 qt_fixed_to_real(offset * 2)),
515 l1_on_x + 90, -sweepLength,
516 curves, &point_count);
520 Q_UNUSED(curve_start);
522 for (
int i=0; i<point_count; i+=3) {
523 emitCubicTo(qt_real_to_fixed(curves[i].x()),
524 qt_real_to_fixed(curves[i].y()),
525 qt_real_to_fixed(curves[i+1].x()),
526 qt_real_to_fixed(curves[i+1].y()),
527 qt_real_to_fixed(curves[i+2].x()),
528 qt_real_to_fixed(curves[i+2].y()));
532 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
536 }
else if (join == RoundCap) {
537 qfixed offset = m_strokeWidth / 2;
540 QLineF l1 = prevLine;
541 qreal dp = QPointF::dotProduct(QPointF(prevLine.dx(), prevLine.dy()), QPointF(nextLine.dx(), nextLine.dy()));
543 l1 = QLineF(prevLine.p2(), prevLine.p1());
545 l1.translate(l1.dx(), l1.dy());
546 l1.setLength(QT_PATH_KAPPA * offset);
549 QLineF l2(qt_fixed_to_real(focal_x), qt_fixed_to_real(focal_y),
550 prevLine.x2(), prevLine.y2());
551 l2.translate(-l2.dy(), l2.dx());
552 l2.setLength(QT_PATH_KAPPA * offset);
554 emitCubicTo(qt_real_to_fixed(l1.x2()),
555 qt_real_to_fixed(l1.y2()),
556 qt_real_to_fixed(l2.x2()),
557 qt_real_to_fixed(l2.y2()),
558 qt_real_to_fixed(l2.x1()),
559 qt_real_to_fixed(l2.y1()));
562 l2 = QLineF(l2.x1(), l2.y1(), l2.x1()-l2.dx(), l2.y1()-l2.dy());
565 l1.translate(nextLine.x1() - l1.x1(), nextLine.y1() - l1.y1());
567 emitCubicTo(qt_real_to_fixed(l2.x2()),
568 qt_real_to_fixed(l2.y2()),
569 qt_real_to_fixed(l1.x2()),
570 qt_real_to_fixed(l1.y2()),
571 qt_real_to_fixed(l1.x1()),
572 qt_real_to_fixed(l1.y1()));
573 }
else if (join == SvgMiterJoin) {
574 QLineF shortCut(prevLine.p2(), nextLine.p1());
575 qreal angle = shortCut.angleTo(prevLine);
576 if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
577 emitLineTo(focal_x, focal_y);
578 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
581 QLineF miterLine(QPointF(qt_fixed_to_real(focal_x),
582 qt_fixed_to_real(focal_y)), isect);
583 if (type == QLineF::NoIntersection || miterLine.length() > qt_fixed_to_real(m_strokeWidth * m_miterLimit) / 2) {
584 emitLineTo(qt_real_to_fixed(nextLine.x1()),
585 qt_real_to_fixed(nextLine.y1()));
587 emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
588 emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
591 Q_ASSERT(!
"QStroker::joinPoints(), bad join style...");
607 QLineF *startTangent)
610 const int MAX_OFFSET = 16;
611 QBezier offsetCurves[MAX_OFFSET];
613 Q_ASSERT(it->hasNext());
614 QStrokerOps::Element first_element = it->next();
615 Q_ASSERT(first_element.isMoveTo());
617 qfixed2d start = first_element;
619#ifdef QPP_STROKE_DEBUG
620 qDebug(
" -> (side) [%.2f, %.2f], startPos=%d",
621 qt_fixed_to_real(start.x),
622 qt_fixed_to_real(start.y));
625 qfixed2d prev = start;
629 qfixed offset = stroker->strokeWidth() / 2;
631 while (it->hasNext()) {
632 QStrokerOps::Element e = it->next();
636#ifdef QPP_STROKE_DEBUG
637 qDebug(
"\n ---> (side) lineto [%.2f, %.2f]", e.x, e.y);
639 QLineF line(qt_fixed_to_real(prev.x), qt_fixed_to_real(prev.y),
640 qt_fixed_to_real(e.x), qt_fixed_to_real(e.y));
641 if (line.p1() != line.p2()) {
642 QLineF normal = line.normalVector();
643 normal.setLength(offset);
644 line.translate(normal.dx(), normal.dy());
649 stroker->joinPoints(prev.x, prev.y, line, stroker->capStyleMode());
651 stroker->emitMoveTo(qt_real_to_fixed(line.x1()), qt_real_to_fixed(line.y1()));
652 *startTangent = line;
655 stroker->joinPoints(prev.x, prev.y, line, stroker->joinStyleMode());
659 stroker->emitLineTo(qt_real_to_fixed(line.x2()),
660 qt_real_to_fixed(line.y2()));
665 }
else if (e.isCurveTo()) {
666 QStrokerOps::Element cp2 = it->next();
667 QStrokerOps::Element ep = it->next();
669#ifdef QPP_STROKE_DEBUG
670 qDebug(
"\n ---> (side) cubicTo [%.2f, %.2f]",
671 qt_fixed_to_real(ep.x),
672 qt_fixed_to_real(ep.y));
676 QBezier::fromPoints(QPointF(qt_fixed_to_real(prev.x), qt_fixed_to_real(prev.y)),
677 QPointF(qt_fixed_to_real(e.x), qt_fixed_to_real(e.y)),
678 QPointF(qt_fixed_to_real(cp2.x), qt_fixed_to_real(cp2.y)),
679 QPointF(qt_fixed_to_real(ep.x), qt_fixed_to_real(ep.y)));
680 int count = bezier.shifted(offsetCurves,
683 stroker->curveThreshold());
687 QLineF tangent = bezier.startTangent();
688 tangent.translate(offsetCurves[0].pt1() - bezier.pt1());
690 QPointF pt = offsetCurves[0].pt1();
692 stroker->joinPoints(prev.x, prev.y,
694 stroker->capStyleMode());
696 stroker->emitMoveTo(qt_real_to_fixed(pt.x()),
697 qt_real_to_fixed(pt.y()));
699 *startTangent = tangent;
702 stroker->joinPoints(prev.x, prev.y,
704 stroker->joinStyleMode());
708 for (
int i=0; i<count; ++i) {
709 QPointF cp1 = offsetCurves[i].pt2();
710 QPointF cp2 = offsetCurves[i].pt3();
711 QPointF ep = offsetCurves[i].pt4();
712 stroker->emitCubicTo(qt_real_to_fixed(cp1.x()), qt_real_to_fixed(cp1.y()),
713 qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
714 qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
722 if (start == prev && !stroker->forceOpen()) {
724#ifdef QPP_STROKE_DEBUG
725 qDebug(
"\n ---> (side) closed subpath");
729 stroker->joinPoints(prev.x, prev.y, *startTangent, stroker->joinStyleMode());
732#ifdef QPP_STROKE_DEBUG
733 qDebug(
"\n ---> (side) open subpath");
825 QPointF *curves,
int *point_count)
827 Q_ASSERT(point_count);
831 if (qt_is_nan(rect.x()) || qt_is_nan(rect.y()) || qt_is_nan(rect.width()) || qt_is_nan(rect.height())
832 || qt_is_nan(startAngle) || qt_is_nan(sweepLength)) {
833 qWarning(
"QPainterPath::arcTo: Adding arc where a parameter is NaN, results are undefined");
844 qreal w = rect.width();
845 qreal w2 = rect.width() / 2;
846 qreal w2k = w2 * QT_PATH_KAPPA;
848 qreal h = rect.height();
849 qreal h2 = rect.height() / 2;
850 qreal h2k = h2 * QT_PATH_KAPPA;
855 QPointF(x + w, y + h2),
858 QPointF(x + w, y + h2 + h2k),
859 QPointF(x + w2 + w2k, y + h),
860 QPointF(x + w2, y + h),
863 QPointF(x + w2 - w2k, y + h),
864 QPointF(x, y + h2 + h2k),
868 QPointF(x, y + h2 - h2k),
869 QPointF(x + w2 - w2k, y),
873 QPointF(x + w2 + w2k, y),
874 QPointF(x + w, y + h2 - h2k),
875 QPointF(x + w, y + h2)
878 if (sweepLength > 360) sweepLength = 360;
879 else if (sweepLength < -360) sweepLength = -360;
882 if (startAngle == 0.0) {
883 if (sweepLength == 360.0) {
884 for (
int i = 11; i >= 0; --i)
885 curves[(*point_count)++] = points[i];
887 }
else if (sweepLength == -360.0) {
888 for (
int i = 1; i <= 12; ++i)
889 curves[(*point_count)++] = points[i];
894 int startSegment =
int(qFloor(startAngle / 90));
895 int endSegment =
int(qFloor((startAngle + sweepLength) / 90));
897 qreal startT = (startAngle - startSegment * 90) / 90;
898 qreal endT = (startAngle + sweepLength - endSegment * 90) / 90;
900 int delta = sweepLength > 0 ? 1 : -1;
907 if (qFuzzyIsNull(startT - qreal(1))) {
909 startSegment += delta;
913 if (qFuzzyIsNull(endT)) {
918 startT = qt_t_for_arc_angle(startT * 90);
919 endT = qt_t_for_arc_angle(endT * 90);
921 const bool splitAtStart = !qFuzzyIsNull(startT);
922 const bool splitAtEnd = !qFuzzyIsNull(endT - qreal(1));
924 const int end = endSegment + delta;
927 if (startSegment == end) {
928 const int quadrant = 3 - ((startSegment % 4) + 4) % 4;
929 const int j = 3 * quadrant;
930 return delta > 0 ? points[j + 3] : points[j];
933 QPointF startPoint, endPoint;
934 qt_find_ellipse_coords(rect, startAngle, sweepLength, &startPoint, &endPoint);
936 for (
int i = startSegment; i != end; i += delta) {
937 const int quadrant = 3 - ((i % 4) + 4) % 4;
938 const int j = 3 * quadrant;
942 b = QBezier::fromPoints(points[j + 3], points[j + 2], points[j + 1], points[j]);
944 b = QBezier::fromPoints(points[j], points[j + 1], points[j + 2], points[j + 3]);
947 if (startSegment == endSegment && qFuzzyCompare(startT, endT))
950 if (i == startSegment) {
951 if (i == endSegment && splitAtEnd)
952 b = b.bezierOnInterval(startT, endT);
953 else if (splitAtStart)
954 b = b.bezierOnInterval(startT, 1);
955 }
else if (i == endSegment && splitAtEnd) {
956 b = b.bezierOnInterval(0, endT);
960 curves[(*point_count)++] = b.pt2();
961 curves[(*point_count)++] = b.pt3();
962 curves[(*point_count)++] = b.pt4();
965 Q_ASSERT(*point_count > 0);
966 curves[*(point_count)-1] = endPoint;
1073void QDashStroker::processCurrentSubpath()
1075 int dashCount = qMin(m_dashPattern.size(), 32);
1079 m_customData = m_stroker;
1080 m_stroke_width = m_stroker->strokeWidth();
1081 m_miter_limit = m_stroker->miterLimit();
1084 qreal longestLength = 0;
1085 qreal sumLength = 0;
1086 for (
int i=0; i<dashCount; ++i) {
1087 dashes[i] = qMax(m_dashPattern.at(i), qreal(0)) * m_stroke_width;
1088 sumLength += dashes[i];
1089 if (dashes[i] > longestLength)
1090 longestLength = dashes[i];
1093 if (qFuzzyIsNull(sumLength))
1096 qreal invSumLength = qreal(1) / sumLength;
1098 Q_ASSERT(dashCount > 0);
1100 dashCount = dashCount & -2;
1105 qreal doffset = m_dashOffset * m_stroke_width;
1108 doffset = std::fmod(doffset, sumLength);
1110 doffset += sumLength;
1112 while (doffset >= dashes[idash]) {
1113 doffset -= dashes[idash];
1114 if (++idash >= dashCount)
1123 QSubpathFlatIterator it(&m_elements, m_dashThreshold);
1124 qfixed2d prev = it.next();
1125 if (!prev.isFinite())
1128 bool clipping = !m_clip_rect.isEmpty();
1129 qfixed2d move_to_pos = prev;
1130 qfixed2d line_to_pos;
1133 qfixed padding = qt_real_to_fixed(qMax(m_stroke_width, m_miter_limit) * longestLength);
1134 qfixed2d clip_tl = { qt_real_to_fixed(m_clip_rect.left()) - padding,
1135 qt_real_to_fixed(m_clip_rect.top()) - padding };
1136 qfixed2d clip_br = { qt_real_to_fixed(m_clip_rect.right()) + padding ,
1137 qt_real_to_fixed(m_clip_rect.bottom()) + padding };
1139 bool hasMoveTo =
false;
1140 while (it.hasNext()) {
1141 QStrokerOps::Element e = it.next();
1142 if (!qfixed2d(e).isFinite())
1145 Q_ASSERT(e.isLineTo());
1146 cline = QLineF(qt_fixed_to_real(prev.x),
1147 qt_fixed_to_real(prev.y),
1148 qt_fixed_to_real(e.x),
1149 qt_fixed_to_real(e.y));
1150 elen = cline.length();
1152 estop = estart + elen;
1154 bool done = pos >= estop;
1157 bool clipIt = clipping && !lineIntersectsRect(prev, e, clip_tl, clip_br);
1158 bool skipDashing = elen * invSumLength > repetitionLimit();
1159 int maxDashes = dashCount;
1160 if (skipDashing || clipIt) {
1162 elen -= std::floor(elen * invSumLength) * sumLength;
1166 qreal dpos = (pos + dashes[idash]) - (doffset + estart);
1168 Q_ASSERT(dpos >= 0);
1171 doffset = dashes[idash] - (dpos - elen);
1175 pos = --maxDashes > 0 ? dpos + estart : estop;
1176 done = pos >= estop;
1177 if (++idash >= dashCount)
1187 emitMoveTo(move_to_pos.x, move_to_pos.y);
1190 emitLineTo(e.x, e.y);
1199 bool has_offset = doffset > 0;
1200 bool evenDash = (idash & 1) == 0;
1202 qreal dpos = (pos + dashes[idash]) - (doffset + estart);
1204 Q_ASSERT(dpos >= 0);
1207 doffset = dashes[idash] - (dpos - elen);
1212 p2 = cline.pointAt(dpos/elen);
1213 pos = dpos + estart;
1214 done = pos >= estop;
1215 if (++idash >= dashCount)
1221 line_to_pos.x = qt_real_to_fixed(p2.x());
1222 line_to_pos.y = qt_real_to_fixed(p2.y());
1225 || lineRectIntersectsRect(move_to_pos, line_to_pos, clip_tl, clip_br))
1231 if (!has_offset || !hasMoveTo) {
1232 emitMoveTo(move_to_pos.x, move_to_pos.y);
1236 emitLineTo(line_to_pos.x, line_to_pos.y);
1240 move_to_pos = line_to_pos;
1242 move_to_pos.x = qt_real_to_fixed(p2.x());
1243 move_to_pos.y = qt_real_to_fixed(p2.y());