10#define CURVE_FLATNESS Q_PI / 8
15void QTriangulatingStroker::endCapOrJoinClosed(
const qreal *start,
const qreal *cur,
16 bool implicitClose,
bool endsAtStart)
21 }
else if (implicitClose) {
28 int count = m_vertices.size();
33 float x = m_vertices.at(count-2);
34 float y = m_vertices.at(count-1);
41 while ((*pts + 2) < endPts &&
float((*pts)[0]) ==
float((*pts)[2])
42 &&
float((*pts)[1]) ==
float((*pts)[3]))
48void QTriangulatingStroker::process(
const QVectorPath &path,
const QPen &pen,
const QRectF &, QPainter::RenderHints)
50 const qreal *pts = path.points();
51 const QPainterPath::ElementType *types = path.elements();
52 int count = path.elementCount();
57 float realWidth = qpen_widthf(pen);
61 m_width = realWidth / 2;
63 bool cosmetic = pen.isCosmetic();
65 m_width = m_width * m_inv_scale;
68 m_join_style = qpen_joinStyle(pen);
69 m_cap_style = qpen_capStyle(pen);
70 m_miter_limit = pen.miterLimit() * qpen_widthf(pen);
92 if (realWidth < 2.5 && (cosmetic || m_inv_scale == 1)) {
93 if (m_cap_style == Qt::RoundCap)
94 m_cap_style = Qt::SquareCap;
95 if (m_join_style == Qt::RoundJoin)
96 m_join_style = Qt::MiterJoin;
97 m_curvyness_add = 0.5;
100 }
else if (cosmetic) {
101 m_curvyness_add = realWidth / 2;
105 m_curvyness_add = m_width;
107 m_roundness = qMax<
int>(4, realWidth * m_curvyness_mul);
112 if (m_roundness > 24)
115 m_sin_theta = qFastSin(Q_PI / m_roundness);
116 m_cos_theta = qFastCos(Q_PI / m_roundness);
118 const qreal *endPts = pts + (count<<1);
119 const qreal *startPts =
nullptr;
121 Qt::PenCapStyle cap = m_cap_style;
124 skipDuplicatePoints(&pts, endPts);
125 if ((pts + 2) == endPts)
130 bool endsAtStart =
float(startPts[0]) ==
float(endPts[-2])
131 &&
float(startPts[1]) ==
float(endPts[-1]);
133 if (endsAtStart || path.hasImplicitClose())
134 m_cap_style = Qt::FlatCap;
138 skipDuplicatePoints(&pts, endPts);
141 skipDuplicatePoints(&pts, endPts);
142 while (pts < endPts) {
146 skipDuplicatePoints(&pts, endPts);
148 endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);
151 bool endsAtStart =
false;
152 QPainterPath::ElementType previousType = QPainterPath::MoveToElement;
153 const qreal *previousPts = pts;
154 while (pts < endPts) {
156 case QPainterPath::MoveToElement: {
157 int end = (endPts - pts) / 2;
158 int nextMoveElement = 1;
159 bool hasValidLineSegments =
false;
160 while (nextMoveElement < end && types[nextMoveElement] != QPainterPath::MoveToElement) {
161 if (!hasValidLineSegments) {
162 hasValidLineSegments =
163 float(pts[0]) !=
float(pts[nextMoveElement * 2]) ||
164 float(pts[1]) !=
float(pts[nextMoveElement * 2 + 1]);
170
171
172
173
174
175
176 if (!hasValidLineSegments) {
177 pts += 2 * nextMoveElement;
178 types += nextMoveElement;
182 if (previousType != QPainterPath::MoveToElement)
183 endCapOrJoinClosed(startPts, previousPts, path.hasImplicitClose(), endsAtStart);
186 skipDuplicatePoints(&startPts, endPts);
187 if (startPts + 2 >= endPts)
190 endsAtStart =
float(startPts[0]) ==
float(pts[nextMoveElement * 2 - 2])
191 &&
float(startPts[1]) ==
float(pts[nextMoveElement * 2 - 1]);
192 if (endsAtStart || path.hasImplicitClose())
193 m_cap_style = Qt::FlatCap;
197 previousType = QPainterPath::MoveToElement;
202 case QPainterPath::LineToElement:
203 if (
float(m_cx) !=
float(pts[0]) ||
float(m_cy) !=
float(pts[1])) {
204 if (previousType != QPainterPath::MoveToElement)
207 previousType = QPainterPath::LineToElement;
213 case QPainterPath::CurveToElement:
214 if (
float(m_cx) !=
float(pts[0]) ||
float(m_cy) !=
float(pts[1])
215 ||
float(pts[0]) !=
float(pts[2]) ||
float(pts[1]) !=
float(pts[3])
216 ||
float(pts[2]) !=
float(pts[4]) ||
float(pts[3]) !=
float(pts[5]))
218 if (
float(m_cx) !=
float(pts[0]) ||
float(m_cy) !=
float(pts[1])) {
219 if (previousType != QPainterPath::MoveToElement)
223 previousType = QPainterPath::CurveToElement;
224 previousPts = pts + 4;
235 if (previousType != QPainterPath::MoveToElement)
236 endCapOrJoinClosed(startPts, previousPts, path.hasImplicitClose(), endsAtStart);
240void QTriangulatingStroker::moveTo(
const qreal *pts)
247 normalVector(m_cx, m_cy, x2, y2, &m_nvx, &m_nvy);
253 bool invisibleJump = m_vertices.size();
255 switch (m_cap_style) {
258 m_vertices.add(m_cx + m_nvx);
259 m_vertices.add(m_cy + m_nvy);
262 case Qt::SquareCap: {
263 float sx = m_cx - m_nvy;
264 float sy = m_cy + m_nvx;
266 m_vertices.add(sx + m_nvx);
267 m_vertices.add(sy + m_nvy);
269 emitLineSegment(sx, sy, m_nvx, m_nvy);
272 QVarLengthArray<
float> points;
273 arcPoints(m_cx, m_cy, m_cx + m_nvx, m_cy + m_nvy, m_cx - m_nvx, m_cy - m_nvy, points);
274 m_vertices.resize(m_vertices.size() + points.size() + 2 *
int(invisibleJump));
275 int count = m_vertices.size();
277 int end = points.size() / 2;
278 while (front != end) {
279 m_vertices.at(--count) = points[2 * end - 1];
280 m_vertices.at(--count) = points[2 * end - 2];
284 m_vertices.at(--count) = points[2 * front + 1];
285 m_vertices.at(--count) = points[2 * front + 0];
290 m_vertices.at(count - 1) = m_vertices.at(count + 1);
291 m_vertices.at(count - 2) = m_vertices.at(count + 0);
296 emitLineSegment(m_cx, m_cy, m_nvx, m_nvy);
299void QTriangulatingStroker::cubicTo(
const qreal *pts)
301 const QPointF *p = (
const QPointF *) pts;
302 QBezier bezier = QBezier::fromPoints(*(p - 1), p[0], p[1], p[2]);
304 QRectF bounds = bezier.bounds();
305 float rad = qMax(bounds.width(), bounds.height());
306 int threshold = qMin<
float>(64, (rad + m_curvyness_add) * m_curvyness_mul);
309 qreal threshold_minus_1 = threshold - 1;
310 float vx = 0, vy = 0;
312 float cx = m_cx, cy = m_cy;
315 for (
int i=1; i<threshold; ++i) {
316 qreal t = qreal(i) / threshold_minus_1;
317 QPointF p = bezier.pointAt(t);
321 normalVector(cx, cy, x, y, &vx, &vy);
323 emitLineSegment(x, y, vx, vy);
336void QTriangulatingStroker::join(
const qreal *pts)
339 normalVector(m_cx, m_cy, pts[0], pts[1], &m_nvx, &m_nvy);
341 switch (m_join_style) {
344 case Qt::SvgMiterJoin:
345 case Qt::MiterJoin: {
347 int count = m_vertices.size();
348 float prevNvx = m_vertices.at(count - 2) - m_cx;
349 float prevNvy = m_vertices.at(count - 1) - m_cy;
350 float xprod = prevNvx * m_nvy - prevNvy * m_nvx;
351 float px, py, qx, qy;
354 if (qFuzzyIsNull(xprod))
359 px = m_vertices.at(count - 2);
360 py = m_vertices.at(count - 1);
364 px = m_vertices.at(count - 4);
365 py = m_vertices.at(count - 3);
371 float pu = px * prevNvx + py * prevNvy;
372 float qv = qx * m_nvx + qy * m_nvy;
373 float ix = (m_nvy * pu - prevNvy * qv) / xprod;
374 float iy = (prevNvx * qv - m_nvx * pu) / xprod;
377 if ((ix - px) * (ix - px) + (iy - py) * (iy - py) <= m_miter_limit * m_miter_limit) {
390 case Qt::RoundJoin: {
391 QVarLengthArray<
float> points;
392 int count = m_vertices.size();
393 float prevNvx = m_vertices.at(count - 2) - m_cx;
394 float prevNvy = m_vertices.at(count - 1) - m_cy;
395 if (m_nvx * prevNvy - m_nvy * prevNvx < 0) {
396 arcPoints(0, 0, m_nvx, m_nvy, -prevNvx, -prevNvy, points);
397 for (
int i = points.size() / 2; i > 0; --i)
398 emitLineSegment(m_cx, m_cy, points[2 * i - 2], points[2 * i - 1]);
400 arcPoints(0, 0, -prevNvx, -prevNvy, m_nvx, m_nvy, points);
401 for (
int i = 0; i < points.size() / 2; ++i)
402 emitLineSegment(m_cx, m_cy, points[2 * i + 0], points[2 * i + 1]);
408 emitLineSegment(m_cx, m_cy, m_nvx, m_nvy);
411void QTriangulatingStroker::endCap(
const qreal *)
413 switch (m_cap_style) {
417 emitLineSegment(m_cx + m_nvy, m_cy - m_nvx, m_nvx, m_nvy);
420 QVarLengthArray<
float> points;
421 int count = m_vertices.size();
422 arcPoints(m_cx, m_cy, m_vertices.at(count - 2), m_vertices.at(count - 1), m_vertices.at(count - 4), m_vertices.at(count - 3), points);
424 int end = points.size() / 2;
425 while (front != end) {
426 m_vertices.add(points[2 * end - 2]);
427 m_vertices.add(points[2 * end - 1]);
431 m_vertices.add(points[2 * front + 0]);
432 m_vertices.add(points[2 * front + 1]);
440void QTriangulatingStroker::arcPoints(
float cx,
float cy,
float fromX,
float fromY,
float toX,
float toY, QVarLengthArray<
float> &points)
442 float dx1 = fromX - cx;
443 float dy1 = fromY - cy;
444 float dx2 = toX - cx;
445 float dy2 = toY - cy;
448 while (dx1 * dy2 - dx2 * dy1 < 0) {
449 float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta;
450 float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta;
453 points.append(cx + dx1);
454 points.append(cy + dy1);
458 while (dx1 * dx2 + dy1 * dy2 < 0) {
459 float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta;
460 float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta;
463 points.append(cx + dx1);
464 points.append(cy + dy1);
468 while (dx1 * dy2 - dx2 * dy1 > 0) {
469 float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta;
470 float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta;
473 points.append(cx + dx1);
474 points.append(cy + dy1);
478 if (!points.isEmpty())
479 points.resize(points.size() - 2);
484 ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::MoveToElement, x, y);
489 ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::LineToElement, x, y);
497QDashedStrokeProcessor::QDashedStrokeProcessor()
498 : m_points(0), m_types(0),
499 m_dash_stroker(
nullptr), m_inv_scale(1)
501 m_dash_stroker.setMoveToHook(qdashprocessor_moveTo);
502 m_dash_stroker.setLineToHook(qdashprocessor_lineTo);
503 m_dash_stroker.setCubicToHook(qdashprocessor_cubicTo);
506void QDashedStrokeProcessor::process(
const QVectorPath &path,
const QPen &pen,
const QRectF &clip, QPainter::RenderHints)
509 const qreal *pts = path.points();
510 const QPainterPath::ElementType *types = path.elements();
511 int count = path.elementCount();
513 bool cosmetic = pen.isCosmetic();
514 bool implicitClose = path.hasImplicitClose();
518 m_points.reserve(path.elementCount());
519 m_types.reserve(path.elementCount());
521 qreal width = qpen_widthf(pen);
525 m_dash_stroker.setDashPattern(pen.dashPattern());
526 m_dash_stroker.setStrokeWidth(cosmetic ? width * m_inv_scale : width);
527 m_dash_stroker.setDashOffset(pen.dashOffset());
528 m_dash_stroker.setMiterLimit(pen.miterLimit());
529 m_dash_stroker.setClipRect(clip);
531 float curvynessAdd, curvynessMul;
534 if (width < 2.5 && (cosmetic || m_inv_scale == 1)) {
537 }
else if (cosmetic) {
538 curvynessAdd= width / 2;
541 curvynessAdd = width * m_inv_scale;
548 bool needsClose =
false;
550 if (pts[0] != pts[count * 2 - 2] || pts[1] != pts[count * 2 - 1])
554 const qreal *firstPts = pts;
555 const qreal *endPts = pts + (count<<1);
556 m_dash_stroker.begin(
this);
559 m_dash_stroker.moveTo(pts[0], pts[1]);
561 while (pts < endPts) {
562 m_dash_stroker.lineTo(pts[0], pts[1]);
566 while (pts < endPts) {
568 case QPainterPath::MoveToElement:
569 m_dash_stroker.moveTo(pts[0], pts[1]);
573 case QPainterPath::LineToElement:
574 m_dash_stroker.lineTo(pts[0], pts[1]);
578 case QPainterPath::CurveToElement: {
579 QBezier b = QBezier::fromPoints(*(((
const QPointF *) pts) - 1),
580 *(((
const QPointF *) pts)),
581 *(((
const QPointF *) pts) + 1),
582 *(((
const QPointF *) pts) + 2));
583 QRectF bounds = b.bounds();
584 float rad = qMax(bounds.width(), bounds.height());
585 int threshold = qMin<
float>(64, (rad + curvynessAdd) * curvynessMul);
589 qreal threshold_minus_1 = threshold - 1;
590 for (
int i=0; i<threshold; ++i) {
591 QPointF pt = b.pointAt(i / threshold_minus_1);
592 m_dash_stroker.lineTo(pt.x(), pt.y());
602 m_dash_stroker.lineTo(firstPts[0], firstPts[1]);
604 m_dash_stroker.end();
Combined button and popup list for selecting options.
static void qdashprocessor_moveTo(qreal x, qreal y, void *data)
static void qdashprocessor_cubicTo(qreal, qreal, qreal, qreal, qreal, qreal, void *)
static void skipDuplicatePoints(const qreal **pts, const qreal *endPts)
static void qdashprocessor_lineTo(qreal x, qreal y, void *data)