void QDashedStrokeProcessor::process(const QVectorPath &path, const QPen &pen, const QRectF &clip, QPainter::RenderHints hints) { const qreal *pts = path.points(); const QPainterPath::ElementType *types = path.elements(); int count = path.elementCount(); bool cosmetic = qt_pen_is_cosmetic(pen, hints); bool implicitClose = path.hasImplicitClose(); m_points.reset(); m_types.reset(); m_points.reserve(path.elementCount()); m_types.reserve(path.elementCount()); qreal width = qpen_widthf(pen); if (width == 0) width = 1; m_dash_stroker.setDashPattern(pen.dashPattern()); m_dash_stroker.setStrokeWidth(cosmetic ? width * m_inv_scale : width); m_dash_stroker.setDashOffset(pen.dashOffset()); m_dash_stroker.setMiterLimit(pen.miterLimit()); m_dash_stroker.setClipRect(clip); float curvynessAdd, curvynessMul; // simplify pens that are thin in device size (2px wide or less) if (width < 2.5 && (cosmetic || m_inv_scale == 1)) { curvynessAdd = 0.5; curvynessMul = CURVE_FLATNESS / m_inv_scale; } else if (cosmetic) { curvynessAdd= width / 2; curvynessMul= float(CURVE_FLATNESS); } else { curvynessAdd = width * m_inv_scale; curvynessMul = CURVE_FLATNESS / m_inv_scale; } if (count < 2) return; bool needsClose = false; if (implicitClose) { if (pts[0] != pts[count * 2 - 2] || pts[1] != pts[count * 2 - 1]) needsClose = true; } const qreal *firstPts = pts; const qreal *endPts = pts + (count<<1); m_dash_stroker.begin(this); if (!types) { m_dash_stroker.moveTo(pts[0], pts[1]); pts += 2; while (pts < endPts) { m_dash_stroker.lineTo(pts[0], pts[1]); pts += 2; } } else { while (pts < endPts) { switch (*types) { case QPainterPath::MoveToElement: m_dash_stroker.moveTo(pts[0], pts[1]); pts += 2; ++types; break; case QPainterPath::LineToElement: m_dash_stroker.lineTo(pts[0], pts[1]); pts += 2; ++types; break; case QPainterPath::CurveToElement: { QBezier b = QBezier::fromPoints(*(((const QPointF *) pts) - 1), *(((const QPointF *) pts)), *(((const QPointF *) pts) + 1), *(((const QPointF *) pts) + 2)); QRectF bounds = b.bounds(); float rad = qMax(bounds.width(), bounds.height()); int threshold = qMin<float>(64, (rad + curvynessAdd) * curvynessMul); if (threshold < 4) threshold = 4; qreal threshold_minus_1 = threshold - 1; for (int i=0; i<threshold; ++i) { QPointF pt = b.pointAt(i / threshold_minus_1); m_dash_stroker.lineTo(pt.x(), pt.y()); } pts += 6; types += 3; break; } default: break; } } } if (needsClose) m_dash_stroker.lineTo(firstPts[0], firstPts[1]); m_dash_stroker.end(); }
void QTriangulatingStroker::process(const QVectorPath &path, const QPen &pen, const QRectF &, QPainter::RenderHints hints) { const qreal *pts = path.points(); const QPainterPath::ElementType *types = path.elements(); int count = path.elementCount(); if (count < 2) return; float realWidth = qpen_widthf(pen); if (realWidth == 0) realWidth = 1; m_width = realWidth / 2; bool cosmetic = qt_pen_is_cosmetic(pen, hints); if (cosmetic) { m_width = m_width * m_inv_scale; } m_join_style = qpen_joinStyle(pen); m_cap_style = qpen_capStyle(pen); m_vertices.reset(); m_miter_limit = pen.miterLimit() * qpen_widthf(pen); // The curvyness is based on the notion that I originally wanted // roughly one line segment pr 4 pixels. This may seem little, but // because we sample at constantly incrementing B(t) E [0<t<1], we // will get longer segments where the curvature is small and smaller // segments when the curvature is high. // // To get a rough idea of the length of each curve, I pretend that // the curve is a 90 degree arc, whose radius is // qMax(curveBounds.width, curveBounds.height). Based on this // logic we can estimate the length of the outline edges based on // the radius + a pen width and adjusting for scale factors // depending on if the pen is cosmetic or not. // // The curvyness value of PI/14 was based on, // arcLength = 2*PI*r/4 = PI*r/2 and splitting length into somewhere // between 3 and 8 where 5 seemed to be give pretty good results // hence: Q_PI/14. Lower divisors will give more detail at the // direct cost of performance. // simplfy pens that are thin in device size (2px wide or less) if (realWidth < 2.5 && (cosmetic || m_inv_scale == 1)) { if (m_cap_style == Qt::RoundCap) m_cap_style = Qt::SquareCap; if (m_join_style == Qt::RoundJoin) m_join_style = Qt::MiterJoin; m_curvyness_add = 0.5; m_curvyness_mul = CURVE_FLATNESS / m_inv_scale; m_roundness = 1; } else if (cosmetic) { m_curvyness_add = realWidth / 2; m_curvyness_mul = float(CURVE_FLATNESS); m_roundness = qMax<int>(4, realWidth * CURVE_FLATNESS); } else { m_curvyness_add = m_width; m_curvyness_mul = CURVE_FLATNESS / m_inv_scale; m_roundness = qMax<int>(4, realWidth * m_curvyness_mul); } // Over this level of segmentation, there doesn't seem to be any // benefit, even for huge penWidth if (m_roundness > 24) m_roundness = 24; m_sin_theta = qFastSin(Q_PI / m_roundness); m_cos_theta = qFastCos(Q_PI / m_roundness); const qreal *endPts = pts + (count<<1); const qreal *startPts = 0; Qt::PenCapStyle cap = m_cap_style; if (!types) { skipDuplicatePoints(&pts, endPts); if ((pts + 2) == endPts) return; startPts = pts; bool endsAtStart = float(startPts[0]) == float(endPts[-2]) && float(startPts[1]) == float(endPts[-1]); if (endsAtStart || path.hasImplicitClose()) m_cap_style = Qt::FlatCap; moveTo(pts); m_cap_style = cap; pts += 2; skipDuplicatePoints(&pts, endPts); lineTo(pts); pts += 2; skipDuplicatePoints(&pts, endPts); while (pts < endPts) { join(pts); lineTo(pts); pts += 2; skipDuplicatePoints(&pts, endPts); } endCapOrJoinClosed(startPts, pts-2, path.hasImplicitClose(), endsAtStart); } else { bool endsAtStart = false; QPainterPath::ElementType previousType = QPainterPath::MoveToElement; const qreal *previousPts = pts; while (pts < endPts) { switch (*types) { case QPainterPath::MoveToElement: { if (previousType != QPainterPath::MoveToElement) endCapOrJoinClosed(startPts, previousPts, path.hasImplicitClose(), endsAtStart); startPts = pts; skipDuplicatePoints(&startPts, endPts); // Skip duplicates to find correct normal. if (startPts + 2 >= endPts) return; // Nothing to see here... int end = (endPts - pts) / 2; int i = 2; // Start looking to ahead since we never have two moveto's in a row while (i<end && types[i] != QPainterPath::MoveToElement) { ++i; } endsAtStart = float(startPts[0]) == float(pts[i*2 - 2]) && float(startPts[1]) == float(pts[i*2 - 1]); if (endsAtStart || path.hasImplicitClose()) m_cap_style = Qt::FlatCap; moveTo(startPts); m_cap_style = cap; previousType = QPainterPath::MoveToElement; previousPts = pts; pts+=2; ++types; break; } case QPainterPath::LineToElement: if (float(m_cx) != float(pts[0]) || float(m_cy) != float(pts[1])) { if (previousType != QPainterPath::MoveToElement) join(pts); lineTo(pts); previousType = QPainterPath::LineToElement; previousPts = pts; } pts+=2; ++types; break; case QPainterPath::CurveToElement: if (float(m_cx) != float(pts[0]) || float(m_cy) != float(pts[1]) || float(pts[0]) != float(pts[2]) || float(pts[1]) != float(pts[3]) || float(pts[2]) != float(pts[4]) || float(pts[3]) != float(pts[5])) { if (float(m_cx) != float(pts[0]) || float(m_cy) != float(pts[1])) { if (previousType != QPainterPath::MoveToElement) join(pts); } cubicTo(pts); previousType = QPainterPath::CurveToElement; previousPts = pts + 4; } pts+=6; types+=3; break; default: Q_ASSERT(false); break; } } if (previousType != QPainterPath::MoveToElement) endCapOrJoinClosed(startPts, previousPts, path.hasImplicitClose(), endsAtStart); } }
void QDashedStrokeProcessor::process(const QVectorPath &path, const QPen &pen, const QRectF &clip) { const qreal *pts = path.points(); const QPainterPath::ElementType *types = path.elements(); int count = path.elementCount(); m_points.reset(); m_types.reset(); qreal width = qpen_widthf(pen); if (width == 0) width = 1; m_dash_stroker.setDashPattern(pen.dashPattern()); m_dash_stroker.setStrokeWidth(pen.isCosmetic() ? width * m_inv_scale : width); m_dash_stroker.setMiterLimit(pen.miterLimit()); m_dash_stroker.setClipRect(clip); qreal curvyness = sqrt(width) * m_inv_scale / 8; if (count < 2) return; const qreal *endPts = pts + (count<<1); m_dash_stroker.begin(this); if (!types) { m_dash_stroker.moveTo(pts[0], pts[1]); pts += 2; while (pts < endPts) { m_dash_stroker.lineTo(pts[0], pts[1]); pts += 2; } } else { while (pts < endPts) { switch (*types) { case QPainterPath::MoveToElement: m_dash_stroker.moveTo(pts[0], pts[1]); pts += 2; ++types; break; case QPainterPath::LineToElement: m_dash_stroker.lineTo(pts[0], pts[1]); pts += 2; ++types; break; case QPainterPath::CurveToElement: { QBezier b = QBezier::fromPoints(*(((const QPointF *) pts) - 1), *(((const QPointF *) pts)), *(((const QPointF *) pts) + 1), *(((const QPointF *) pts) + 2)); QRectF bounds = b.bounds(); int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * curvyness); if (threshold < 4) threshold = 4; qreal threshold_minus_1 = threshold - 1; for (int i=0; i<threshold; ++i) { QPointF pt = b.pointAt(i / threshold_minus_1); m_dash_stroker.lineTo(pt.x(), pt.y()); } pts += 6; types += 3; break; } default: break; } } } m_dash_stroker.end(); }