9#define CURVE_FLATNESS Q_PI / 8
14void QTriangulatingStroker::endCapOrJoinClosed(
const qreal *start,
const qreal *cur,
15 bool implicitClose,
bool endsAtStart)
20 }
else if (implicitClose) {
27 int count = m_vertices.size();
32 float x = m_vertices.at(count-2);
33 float y = m_vertices.at(count-1);
40 while ((*pts + 2) < endPts &&
float((*pts)[0]) ==
float((*pts)[2])
41 &&
float((*pts)[1]) ==
float((*pts)[3]))
47void QTriangulatingStroker::process(
const QVectorPath &path,
const QPen &pen,
const QRectF &, QPainter::RenderHints)
49 const qreal *pts = path.points();
50 const QPainterPath::ElementType *types = path.elements();
51 int count = path.elementCount();
56 float realWidth = qpen_widthf(pen);
60 m_width = realWidth / 2;
62 bool cosmetic = pen.isCosmetic();
64 m_width = m_width * m_inv_scale;
67 m_join_style = qpen_joinStyle(pen);
68 m_cap_style = qpen_capStyle(pen);
69 m_miter_limit = pen.miterLimit() * qpen_widthf(pen);
91 if (realWidth < 2.5 && (cosmetic || m_inv_scale == 1)) {
92 if (m_cap_style == Qt::RoundCap)
93 m_cap_style = Qt::SquareCap;
94 if (m_join_style == Qt::RoundJoin)
95 m_join_style = Qt::MiterJoin;
96 m_curvyness_add = 0.5;
99 }
else if (cosmetic) {
100 m_curvyness_add = realWidth / 2;
104 m_curvyness_add = m_width;
106 m_roundness = qMax<
int>(4, realWidth * m_curvyness_mul);
111 if (m_roundness > 24)
114 m_sin_theta = qFastSin(Q_PI / m_roundness);
115 m_cos_theta = qFastCos(Q_PI / m_roundness);
117 const qreal *endPts = pts + (count<<1);
118 const qreal *startPts =
nullptr;
120 Qt::PenCapStyle cap = m_cap_style;
123 skipDuplicatePoints(&pts, endPts);
124 if ((pts + 2) == endPts)
129 bool endsAtStart =
float(startPts[0]) ==
float(endPts[-2])
130 &&
float(startPts[1]) ==
float(endPts[-1]);
132 if (endsAtStart || path.hasImplicitClose())
133 m_cap_style = Qt::FlatCap;
137 skipDuplicatePoints(&pts, endPts);
140 skipDuplicatePoints(&pts, endPts);
141 while (pts < endPts) {
145 skipDuplicatePoints(&pts, endPts);
147 endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart);
150 bool endsAtStart =
false;
151 QPainterPath::ElementType previousType = QPainterPath::MoveToElement;
152 const qreal *previousPts = pts;
153 while (pts < endPts) {
155 case QPainterPath::MoveToElement: {
156 int end = (endPts - pts) / 2;
157 int nextMoveElement = 1;
158 bool hasValidLineSegments =
false;
159 while (nextMoveElement < end && types[nextMoveElement] != QPainterPath::MoveToElement) {
160 if (!hasValidLineSegments) {
161 hasValidLineSegments =
162 float(pts[0]) !=
float(pts[nextMoveElement * 2]) ||
163 float(pts[1]) !=
float(pts[nextMoveElement * 2 + 1]);
169
170
171
172
173
174
175 if (!hasValidLineSegments) {
176 pts += 2 * nextMoveElement;
177 types += nextMoveElement;
181 if (previousType != QPainterPath::MoveToElement)
182 endCapOrJoinClosed(startPts, previousPts, path.hasImplicitClose(), endsAtStart);
185 skipDuplicatePoints(&startPts, endPts);
186 if (startPts + 2 >= endPts)
189 endsAtStart =
float(startPts[0]) ==
float(pts[nextMoveElement * 2 - 2])
190 &&
float(startPts[1]) ==
float(pts[nextMoveElement * 2 - 1]);
191 if (endsAtStart || path.hasImplicitClose())
192 m_cap_style = Qt::FlatCap;
196 previousType = QPainterPath::MoveToElement;
201 case QPainterPath::LineToElement:
202 if (
float(m_cx) !=
float(pts[0]) ||
float(m_cy) !=
float(pts[1])) {
203 if (previousType != QPainterPath::MoveToElement)
206 previousType = QPainterPath::LineToElement;
212 case QPainterPath::CurveToElement:
213 if (
float(m_cx) !=
float(pts[0]) ||
float(m_cy) !=
float(pts[1])
214 ||
float(pts[0]) !=
float(pts[2]) ||
float(pts[1]) !=
float(pts[3])
215 ||
float(pts[2]) !=
float(pts[4]) ||
float(pts[3]) !=
float(pts[5]))
217 if (
float(m_cx) !=
float(pts[0]) ||
float(m_cy) !=
float(pts[1])) {
218 if (previousType != QPainterPath::MoveToElement)
222 previousType = QPainterPath::CurveToElement;
223 previousPts = pts + 4;
234 if (previousType != QPainterPath::MoveToElement)
235 endCapOrJoinClosed(startPts, previousPts, path.hasImplicitClose(), endsAtStart);
239void QTriangulatingStroker::moveTo(
const qreal *pts)
246 normalVector(m_cx, m_cy, x2, y2, &m_nvx, &m_nvy);
252 bool invisibleJump = m_vertices.size();
254 switch (m_cap_style) {
257 m_vertices.add(m_cx + m_nvx);
258 m_vertices.add(m_cy + m_nvy);
261 case Qt::SquareCap: {
262 float sx = m_cx - m_nvy;
263 float sy = m_cy + m_nvx;
265 m_vertices.add(sx + m_nvx);
266 m_vertices.add(sy + m_nvy);
268 emitLineSegment(sx, sy, m_nvx, m_nvy);
271 QVarLengthArray<
float> points;
272 arcPoints(m_cx, m_cy, m_cx + m_nvx, m_cy + m_nvy, m_cx - m_nvx, m_cy - m_nvy, points);
273 m_vertices.resize(m_vertices.size() + points.size() + 2 *
int(invisibleJump));
274 int count = m_vertices.size();
276 int end = points.size() / 2;
277 while (front != end) {
278 m_vertices.at(--count) = points[2 * end - 1];
279 m_vertices.at(--count) = points[2 * end - 2];
283 m_vertices.at(--count) = points[2 * front + 1];
284 m_vertices.at(--count) = points[2 * front + 0];
289 m_vertices.at(count - 1) = m_vertices.at(count + 1);
290 m_vertices.at(count - 2) = m_vertices.at(count + 0);
295 emitLineSegment(m_cx, m_cy, m_nvx, m_nvy);
298void QTriangulatingStroker::cubicTo(
const qreal *pts)
300 const QPointF *p = (
const QPointF *) pts;
301 QBezier bezier = QBezier::fromPoints(*(p - 1), p[0], p[1], p[2]);
303 QRectF bounds = bezier.bounds();
304 float rad = qMax(bounds.width(), bounds.height());
305 int threshold = qMin<
float>(64, (rad + m_curvyness_add) * m_curvyness_mul);
308 qreal threshold_minus_1 = threshold - 1;
309 float vx = 0, vy = 0;
311 float cx = m_cx, cy = m_cy;
314 for (
int i=1; i<threshold; ++i) {
315 qreal t = qreal(i) / threshold_minus_1;
316 QPointF p = bezier.pointAt(t);
320 normalVector(cx, cy, x, y, &vx, &vy);
322 emitLineSegment(x, y, vx, vy);
335void QTriangulatingStroker::join(
const qreal *pts)
338 normalVector(m_cx, m_cy, pts[0], pts[1], &m_nvx, &m_nvy);
340 switch (m_join_style) {
343 case Qt::SvgMiterJoin:
344 case Qt::MiterJoin: {
346 int count = m_vertices.size();
347 float prevNvx = m_vertices.at(count - 2) - m_cx;
348 float prevNvy = m_vertices.at(count - 1) - m_cy;
349 float xprod = prevNvx * m_nvy - prevNvy * m_nvx;
350 float px, py, qx, qy;
353 if (qFuzzyIsNull(xprod))
358 px = m_vertices.at(count - 2);
359 py = m_vertices.at(count - 1);
363 px = m_vertices.at(count - 4);
364 py = m_vertices.at(count - 3);
370 float pu = px * prevNvx + py * prevNvy;
371 float qv = qx * m_nvx + qy * m_nvy;
372 float ix = (m_nvy * pu - prevNvy * qv) / xprod;
373 float iy = (prevNvx * qv - m_nvx * pu) / xprod;
376 if ((ix - px) * (ix - px) + (iy - py) * (iy - py) <= m_miter_limit * m_miter_limit) {
389 case Qt::RoundJoin: {
390 QVarLengthArray<
float> points;
391 int count = m_vertices.size();
392 float prevNvx = m_vertices.at(count - 2) - m_cx;
393 float prevNvy = m_vertices.at(count - 1) - m_cy;
394 if (m_nvx * prevNvy - m_nvy * prevNvx < 0) {
395 arcPoints(0, 0, m_nvx, m_nvy, -prevNvx, -prevNvy, points);
396 for (
int i = points.size() / 2; i > 0; --i)
397 emitLineSegment(m_cx, m_cy, points[2 * i - 2], points[2 * i - 1]);
399 arcPoints(0, 0, -prevNvx, -prevNvy, m_nvx, m_nvy, points);
400 for (
int i = 0; i < points.size() / 2; ++i)
401 emitLineSegment(m_cx, m_cy, points[2 * i + 0], points[2 * i + 1]);
407 emitLineSegment(m_cx, m_cy, m_nvx, m_nvy);
410void QTriangulatingStroker::endCap(
const qreal *)
412 switch (m_cap_style) {
416 emitLineSegment(m_cx + m_nvy, m_cy - m_nvx, m_nvx, m_nvy);
419 QVarLengthArray<
float> points;
420 int count = m_vertices.size();
421 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);
423 int end = points.size() / 2;
424 while (front != end) {
425 m_vertices.add(points[2 * end - 2]);
426 m_vertices.add(points[2 * end - 1]);
430 m_vertices.add(points[2 * front + 0]);
431 m_vertices.add(points[2 * front + 1]);
439void QTriangulatingStroker::arcPoints(
float cx,
float cy,
float fromX,
float fromY,
float toX,
float toY, QVarLengthArray<
float> &points)
441 float dx1 = fromX - cx;
442 float dy1 = fromY - cy;
443 float dx2 = toX - cx;
444 float dy2 = toY - cy;
447 while (dx1 * dy2 - dx2 * dy1 < 0) {
448 float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta;
449 float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta;
452 points.append(cx + dx1);
453 points.append(cy + dy1);
457 while (dx1 * dx2 + dy1 * dy2 < 0) {
458 float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta;
459 float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta;
462 points.append(cx + dx1);
463 points.append(cy + dy1);
467 while (dx1 * dy2 - dx2 * dy1 > 0) {
468 float tmpx = dx1 * m_cos_theta - dy1 * m_sin_theta;
469 float tmpy = dx1 * m_sin_theta + dy1 * m_cos_theta;
472 points.append(cx + dx1);
473 points.append(cy + dy1);
477 if (!points.isEmpty())
478 points.resize(points.size() - 2);
483 ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::MoveToElement, x, y);
488 ((QDashedStrokeProcessor *) data)->addElement(QPainterPath::LineToElement, x, y);
496QDashedStrokeProcessor::QDashedStrokeProcessor()
497 : m_points(0), m_types(0),
498 m_dash_stroker(
nullptr), m_inv_scale(1)
500 m_dash_stroker.setMoveToHook(qdashprocessor_moveTo);
501 m_dash_stroker.setLineToHook(qdashprocessor_lineTo);
502 m_dash_stroker.setCubicToHook(qdashprocessor_cubicTo);
505void QDashedStrokeProcessor::process(
const QVectorPath &path,
const QPen &pen,
const QRectF &clip, QPainter::RenderHints)
508 const qreal *pts = path.points();
509 const QPainterPath::ElementType *types = path.elements();
510 int count = path.elementCount();
512 bool cosmetic = pen.isCosmetic();
513 bool implicitClose = path.hasImplicitClose();
517 m_points.reserve(path.elementCount());
518 m_types.reserve(path.elementCount());
520 qreal width = qpen_widthf(pen);
524 m_dash_stroker.setDashPattern(pen.dashPattern());
525 m_dash_stroker.setStrokeWidth(cosmetic ? width * m_inv_scale : width);
526 m_dash_stroker.setDashOffset(pen.dashOffset());
527 m_dash_stroker.setMiterLimit(pen.miterLimit());
528 m_dash_stroker.setClipRect(clip);
530 float curvynessAdd, curvynessMul;
533 if (width < 2.5 && (cosmetic || m_inv_scale == 1)) {
536 }
else if (cosmetic) {
537 curvynessAdd= width / 2;
540 curvynessAdd = width * m_inv_scale;
547 bool needsClose =
false;
549 if (pts[0] != pts[count * 2 - 2] || pts[1] != pts[count * 2 - 1])
553 const qreal *firstPts = pts;
554 const qreal *endPts = pts + (count<<1);
555 m_dash_stroker.begin(
this);
558 m_dash_stroker.moveTo(pts[0], pts[1]);
560 while (pts < endPts) {
561 m_dash_stroker.lineTo(pts[0], pts[1]);
565 while (pts < endPts) {
567 case QPainterPath::MoveToElement:
568 m_dash_stroker.moveTo(pts[0], pts[1]);
572 case QPainterPath::LineToElement:
573 m_dash_stroker.lineTo(pts[0], pts[1]);
577 case QPainterPath::CurveToElement: {
578 QBezier b = QBezier::fromPoints(*(((
const QPointF *) pts) - 1),
579 *(((
const QPointF *) pts)),
580 *(((
const QPointF *) pts) + 1),
581 *(((
const QPointF *) pts) + 2));
582 QRectF bounds = b.bounds();
583 float rad = qMax(bounds.width(), bounds.height());
584 int threshold = qMin<
float>(64, (rad + curvynessAdd) * curvynessMul);
588 qreal threshold_minus_1 = threshold - 1;
589 for (
int i=0; i<threshold; ++i) {
590 QPointF pt = b.pointAt(i / threshold_minus_1);
591 m_dash_stroker.lineTo(pt.x(), pt.y());
601 m_dash_stroker.lineTo(firstPts[0], firstPts[1]);
603 m_dash_stroker.end();
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)