qreal QBezier::tAtLength(qreal l) const { qreal len = length(); qreal t = qreal(1.0); const qreal error = qreal(0.01); if (l > len || qFuzzyCompare(l, len)) return t; t *= qreal(0.5); //int iters = 0; //qDebug()<<"LEN is "<<l<<len; qreal lastBigger = qreal(1.0); while (1) { //qDebug()<<"\tt is "<<t; QBezier right = *this; QBezier left; right.parameterSplitLeft(t, &left); qreal lLen = left.length(); if (qAbs(lLen - l) < error) break; if (lLen < l) { t += (lastBigger - t) * qreal(0.5); } else { lastBigger = t; t -= t * qreal(0.5); } //++iters; } //qDebug()<<"number of iters is "<<iters; return t; }
void QBezier::addToPolygon(QPolygonF *polygon, qreal bezier_flattening_threshold) const { QBezier beziers[32]; beziers[0] = *this; QBezier *b = beziers; while (b >= beziers) { // check if we can pop the top bezier curve from the stack qreal y4y1 = b->y4 - b->y1; qreal x4x1 = b->x4 - b->x1; qreal l = qAbs(x4x1) + qAbs(y4y1); qreal d; if (l > 1.) { d = qAbs( (x4x1)*(b->y1 - b->y2) - (y4y1)*(b->x1 - b->x2) ) + qAbs( (x4x1)*(b->y1 - b->y3) - (y4y1)*(b->x1 - b->x3) ); } else { d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) + qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3); l = 1.; } if (d < bezier_flattening_threshold*l || b == beziers + 31) { // good enough, we pop it off and add the endpoint polygon->append(QPointF(b->x4, b->y4)); --b; } else { // split, second half of the polygon goes lower into the stack b->split(b+1, b); ++b; } } }
int QBezier::shifted(QBezier *curveSegments, int maxSegments, qreal offset, float threshold) const { Q_ASSERT(curveSegments); Q_ASSERT(maxSegments > 0); if (x1 == x2 && x1 == x3 && x1 == x4 && y1 == y2 && y1 == y3 && y1 == y4) return 0; --maxSegments; QBezier beziers[10]; redo: beziers[0] = *this; QBezier *b = beziers; QBezier *o = curveSegments; while (b >= beziers) { int stack_segments = b - beziers + 1; if ((stack_segments == 10) || (o - curveSegments == maxSegments - stack_segments)) { threshold *= qreal(1.5); if (threshold > qreal(2.0)) goto give_up; goto redo; } ShiftResult res = shift(b, o, offset, threshold); if (res == Discard) { --b; } else if (res == Ok) { ++o; --b; continue; } else if (res == Circle && maxSegments - (o - curveSegments) >= 2) { // add semi circle if (addCircle(b, offset, o)) o += 2; --b; } else { b->split(b+1, b); ++b; } } give_up: while (b >= beziers) { ShiftResult res = shift(b, o, offset, threshold); // if res isn't Ok or Split then *o is undefined if (res == Ok || res == Split) ++o; --b; } Q_ASSERT(o - curveSegments <= maxSegments); return o - curveSegments; }
void QOutlineMapper::curveTo(const QPointF &cp1, const QPointF &cp2, const QPointF &ep) { #ifdef QT_DEBUG_CONVERT printf("QOutlineMapper::curveTo() (%f, %f)\n", ep.x(), ep.y()); #endif QBezier bezier = QBezier::fromPoints(m_elements.last(), cp1, cp2, ep); bezier.addToPolygon(m_elements, m_curve_threshold); m_element_types.reserve(m_elements.size()); for (int i = m_elements.size() - m_element_types.size(); i; --i) m_element_types << QPainterPath::LineToElement; Q_ASSERT(m_elements.size() == m_element_types.size()); }
QBezier QBezier::bezierOnInterval(qreal t0, qreal t1) const { if (t0 == 0 && t1 == 1) return *this; QBezier bezier = *this; QBezier result; bezier.parameterSplitLeft(t0, &result); qreal trueT = (t1-t0)/(1-t0); bezier.parameterSplitLeft(trueT, &result); return result; }
QBezier QBezier::getSubRange(qreal t0, qreal t1) const { QBezier result; QBezier temp; // cut at t1 if (qFuzzyIsNull(t1 - qreal(1.))) { result = *this; } else { temp = *this; temp.parameterSplitLeft(t1, &result); } // cut at t0 if (!qFuzzyIsNull(t0)) result.parameterSplitLeft(t0 / t1, &temp); return result; }
void QBezier::addToPolygon(QDataBuffer<QPointF> &polygon, qreal bezier_flattening_threshold) const { QBezier beziers[10]; int levels[10]; beziers[0] = *this; levels[0] = 9; QBezier *b = beziers; int *lvl = levels; while (b >= beziers) { // check if we can pop the top bezier curve from the stack qreal y4y1 = b->y4 - b->y1; qreal x4x1 = b->x4 - b->x1; qreal l = qAbs(x4x1) + qAbs(y4y1); qreal d; if (l > 1.) { d = qAbs( (x4x1)*(b->y1 - b->y2) - (y4y1)*(b->x1 - b->x2) ) + qAbs( (x4x1)*(b->y1 - b->y3) - (y4y1)*(b->x1 - b->x3) ); } else { d = qAbs(b->x1 - b->x2) + qAbs(b->y1 - b->y2) + qAbs(b->x1 - b->x3) + qAbs(b->y1 - b->y3); l = 1.; } if (d < bezier_flattening_threshold*l || *lvl == 0) { // good enough, we pop it off and add the endpoint polygon.add(QPointF(b->x4, b->y4)); --b; --lvl; } else { // split, second half of the polygon goes lower into the stack b->split(b+1, b); lvl[1] = --lvl[0]; ++b; ++lvl; } } }
void QTriangulatingStroker::cubicTo(const qreal *pts) { const QPointF *p = (const QPointF *) pts; QBezier bezier = QBezier::fromPoints(*(p - 1), p[0], p[1], p[2]); QRectF bounds = bezier.bounds(); float rad = qMax(bounds.width(), bounds.height()); int threshold = qMin<float>(64, (rad + m_curvyness_add) * m_curvyness_mul); if (threshold < 4) threshold = 4; qreal threshold_minus_1 = threshold - 1; float vx, vy; float cx = m_cx, cy = m_cy; float x, y; for (int i=1; i<threshold; ++i) { qreal t = qreal(i) / threshold_minus_1; QPointF p = bezier.pointAt(t); x = p.x(); y = p.y(); normalVector(cx, cy, x, y, &vx, &vy); emitLineSegment(x, y, vx, vy); cx = x; cy = y; } m_cx = cx; m_cy = cy; m_nvx = vx; m_nvy = vy; }
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(); bool cosmetic = pen.isCosmetic(); 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= CURVE_FLATNESS; } else { curvynessAdd = width * m_inv_scale; curvynessMul = CURVE_FLATNESS / m_inv_scale; } 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(); 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; } } } m_dash_stroker.end(); }
void QGL2PEXVertexArray::addPath(const QVectorPath &path, GLfloat curveInverseScale, bool outline) { const QPointF* const points = reinterpret_cast<const QPointF*>(path.points()); const QPainterPath::ElementType* const elements = path.elements(); if (boundingRectDirty) { minX = maxX = points[0].x(); minY = maxY = points[0].y(); boundingRectDirty = false; } if (!outline && !path.isConvex()) addCentroid(path, 0); int lastMoveTo = vertexArray.size(); vertexArray.add(points[0]); // The first element is always a moveTo do { if (!elements) { // qDebug("QVectorPath has no elements"); // If the path has a null elements pointer, the elements implicitly // start with a moveTo (already added) and continue with lineTos: for (int i=1; i<path.elementCount(); ++i) lineToArray(points[i].x(), points[i].y()); break; } // qDebug("QVectorPath has element types"); for (int i=1; i<path.elementCount(); ++i) { switch (elements[i]) { case QPainterPath::MoveToElement: if (!outline) addClosingLine(lastMoveTo); // qDebug("element[%d] is a MoveToElement", i); vertexArrayStops.add(vertexArray.size()); if (!outline) { if (!path.isConvex()) addCentroid(path, i); lastMoveTo = vertexArray.size(); } lineToArray(points[i].x(), points[i].y()); // Add the moveTo as a new vertex break; case QPainterPath::LineToElement: // qDebug("element[%d] is a LineToElement", i); lineToArray(points[i].x(), points[i].y()); break; case QPainterPath::CurveToElement: { QBezier b = QBezier::fromPoints(*(((const QPointF *) points) + i - 1), points[i], points[i+1], points[i+2]); QRectF bounds = b.bounds(); // threshold based on same algorithm as in qtriangulatingstroker.cpp int threshold = qMin<float>(64, qMax(bounds.width(), bounds.height()) * 3.14f / (curveInverseScale * 6)); if (threshold < 3) threshold = 3; qreal one_over_threshold_minus_1 = qreal(1) / (threshold - 1); for (int t=0; t<threshold; ++t) { QPointF pt = b.pointAt(t * one_over_threshold_minus_1); lineToArray(pt.x(), pt.y()); } i += 2; break; } default: break; } } } while (0); if (!outline) addClosingLine(lastMoveTo); vertexArrayStops.add(vertexArray.size()); }
void QDashedStrokeProcessor::process(const QVectorPath &path, const QPen &pen) { 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()); 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(); }