--- a/src/gui/painting/qstroker.cpp Wed Jun 23 19:07:03 2010 +0300
+++ b/src/gui/painting/qstroker.cpp Tue Jul 06 15:10:48 2010 +0300
@@ -120,8 +120,8 @@
class QSubpathFlatIterator
{
public:
- QSubpathFlatIterator(const QDataBuffer<QStrokerOps::Element> *path)
- : m_path(path), m_pos(0), m_curve_index(-1) { }
+ QSubpathFlatIterator(const QDataBuffer<QStrokerOps::Element> *path, qreal threshold)
+ : m_path(path), m_pos(0), m_curve_index(-1), m_curve_threshold(threshold) { }
inline bool hasNext() const { return m_curve_index >= 0 || m_pos < m_path->size(); }
@@ -152,7 +152,7 @@
QPointF(qt_fixed_to_real(m_path->at(m_pos+1).x),
qt_fixed_to_real(m_path->at(m_pos+1).y)),
QPointF(qt_fixed_to_real(m_path->at(m_pos+2).x),
- qt_fixed_to_real(m_path->at(m_pos+2).y))).toPolygon();
+ qt_fixed_to_real(m_path->at(m_pos+2).y))).toPolygon(m_curve_threshold);
m_curve_index = 1;
e.type = QPainterPath::LineToElement;
e.x = m_curve.at(0).x();
@@ -169,6 +169,7 @@
int m_pos;
QPolygonF m_curve;
int m_curve_index;
+ qreal m_curve_threshold;
};
template <class Iterator> bool qt_stroke_side(Iterator *it, QStroker *stroker,
@@ -187,7 +188,12 @@
}
QStrokerOps::QStrokerOps()
- : m_customData(0), m_moveTo(0), m_lineTo(0), m_cubicTo(0)
+ : m_elements(0)
+ , m_curveThreshold(qt_real_to_fixed(0.25))
+ , m_customData(0)
+ , m_moveTo(0)
+ , m_lineTo(0)
+ , m_cubicTo(0)
{
}
@@ -195,7 +201,6 @@
{
}
-
/*!
Prepares the stroker. Call this function once before starting a
stroke by calling moveTo, lineTo or cubicTo.
@@ -238,6 +243,7 @@
if (path.isEmpty())
return;
+ setCurveThresholdFromTransform(matrix);
begin(customData);
int count = path.elementCount();
if (matrix.isIdentity()) {
@@ -308,6 +314,8 @@
{
if (!pointCount)
return;
+
+ setCurveThresholdFromTransform(matrix);
begin(data);
if (matrix.isIdentity()) {
moveTo(qt_real_to_fixed(points[0].x()), qt_real_to_fixed(points[0].y()));
@@ -348,6 +356,7 @@
}
}
+ setCurveThresholdFromTransform(matrix);
begin(data);
moveTo(qt_real_to_fixed(start.x()), qt_real_to_fixed(start.y()));
for (int i=0; i<12; i+=3) {
@@ -366,12 +375,10 @@
{
m_strokeWidth = qt_real_to_fixed(1);
m_miterLimit = qt_real_to_fixed(2);
- m_curveThreshold = qt_real_to_fixed(0.25);
}
QStroker::~QStroker()
{
-
}
Qt::PenCapStyle QStroker::capForJoinMode(LineJoinMode mode)
@@ -1043,6 +1050,47 @@
return pattern;
}
+static inline bool lineRectIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br)
+{
+ return ((p1.x > tl.x || p2.x > tl.x) && (p1.x < br.x || p2.x < br.x)
+ && (p1.y > tl.y || p2.y > tl.y) && (p1.y < br.y || p2.y < br.y));
+}
+
+// If the line intersects the rectangle, this function will return true.
+static bool lineIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br)
+{
+ if (!lineRectIntersectsRect(p1, p2, tl, br))
+ return false;
+ if (p1.x == p2.x || p1.y == p2.y)
+ return true;
+
+ if (p1.y > p2.y)
+ qSwap(p1, p2); // make p1 above p2
+ qfixed2d u;
+ qfixed2d v;
+ qfixed2d w = {p2.x - p1.x, p2.y - p1.y};
+ if (p1.x < p2.x) {
+ // backslash
+ u.x = tl.x - p1.x; u.y = br.y - p1.y;
+ v.x = br.x - p1.x; v.y = tl.y - p1.y;
+ } else {
+ // slash
+ u.x = tl.x - p1.x; u.y = tl.y - p1.y;
+ v.x = br.x - p1.x; v.y = br.y - p1.y;
+ }
+#if defined(QFIXED_IS_26_6) || defined(QFIXED_IS_16_16)
+ qint64 val1 = qint64(u.x) * qint64(w.y) - qint64(u.y) * qint64(w.x);
+ qint64 val2 = qint64(v.x) * qint64(w.y) - qint64(v.y) * qint64(w.x);
+ return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0);
+#elif defined(QFIXED_IS_32_32)
+ // Cannot do proper test because it may overflow.
+ return true;
+#else
+ qreal val1 = u.x * w.y - u.y * w.x;
+ qreal val2 = v.x * w.y - v.y * w.x;
+ return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0);
+#endif
+}
void QDashStroker::processCurrentSubpath()
{
@@ -1067,9 +1115,11 @@
if (qFuzzyIsNull(sumLength))
return;
+ qreal invSumLength = qreal(1) / sumLength;
+
Q_ASSERT(dashCount > 0);
- dashCount = (dashCount / 2) * 2; // Round down to even number
+ dashCount = dashCount & -2; // Round down to even number
int idash = 0; // Index to current dash
qreal pos = 0; // The position on the curve, 0 <= pos <= path.length
@@ -1077,11 +1127,12 @@
qreal doffset = m_dashOffset * m_stroke_width;
// make sure doffset is in range [0..sumLength)
- doffset -= qFloor(doffset / sumLength) * sumLength;
+ doffset -= qFloor(doffset * invSumLength) * sumLength;
while (doffset >= dashes[idash]) {
doffset -= dashes[idash];
- idash = (idash + 1) % dashCount;
+ if (++idash >= dashCount)
+ idash = 0;
}
qreal estart = 0; // The elements starting position
@@ -1091,7 +1142,7 @@
QPainterPath dashPath;
- QSubpathFlatIterator it(&m_elements);
+ QSubpathFlatIterator it(&m_elements, m_curveThreshold);
qfixed2d prev = it.next();
bool clipping = !m_clip_rect.isEmpty();
@@ -1119,12 +1170,41 @@
estop = estart + elen;
bool done = pos >= estop;
+
+ if (clipping) {
+ // Check if the entire line can be clipped away.
+ if (!lineIntersectsRect(prev, e, clip_tl, clip_br)) {
+ // Cut away full dash sequences.
+ elen -= qFloor(elen * invSumLength) * sumLength;
+ // Update dash offset.
+ while (!done) {
+ qreal dpos = pos + dashes[idash] - doffset - estart;
+
+ Q_ASSERT(dpos >= 0);
+
+ if (dpos > elen) { // dash extends this line
+ doffset = dashes[idash] - (dpos - elen); // subtract the part already used
+ pos = estop; // move pos to next path element
+ done = true;
+ } else { // Dash is on this line
+ pos = dpos + estart;
+ done = pos >= estop;
+ if (++idash >= dashCount)
+ idash = 0;
+ doffset = 0; // full segment so no offset on next.
+ }
+ }
+ hasMoveTo = false;
+ move_to_pos = e;
+ }
+ }
+
// Dash away...
while (!done) {
QPointF p2;
- int idash_incr = 0;
bool has_offset = doffset > 0;
+ bool evenDash = (idash & 1) == 0;
qreal dpos = pos + dashes[idash] - doffset - estart;
Q_ASSERT(dpos >= 0);
@@ -1138,39 +1218,36 @@
p2 = cline.pointAt(dpos/elen);
pos = dpos + estart;
done = pos >= estop;
- idash_incr = 1;
+ if (++idash >= dashCount)
+ idash = 0;
doffset = 0; // full segment so no offset on next.
}
- if (idash % 2 == 0) {
+ if (evenDash) {
line_to_pos.x = qt_real_to_fixed(p2.x());
line_to_pos.y = qt_real_to_fixed(p2.y());
- // If we have an offset, we're continuing a dash
- // from a previous element and should only
- // continue the current dash, without starting a
- // new subpath.
- if (!has_offset || !hasMoveTo) {
- emitMoveTo(move_to_pos.x, move_to_pos.y);
- hasMoveTo = true;
- }
+ if (!clipping
+ || lineRectIntersectsRect(move_to_pos, line_to_pos, clip_tl, clip_br))
+ {
+ // If we have an offset, we're continuing a dash
+ // from a previous element and should only
+ // continue the current dash, without starting a
+ // new subpath.
+ if (!has_offset || !hasMoveTo) {
+ emitMoveTo(move_to_pos.x, move_to_pos.y);
+ hasMoveTo = true;
+ }
- if (!clipping
- // if move_to is inside...
- || (move_to_pos.x > clip_tl.x && move_to_pos.x < clip_br.x
- && move_to_pos.y > clip_tl.y && move_to_pos.y < clip_br.y)
- // Or if line_to is inside...
- || (line_to_pos.x > clip_tl.x && line_to_pos.x < clip_br.x
- && line_to_pos.y > clip_tl.y && line_to_pos.y < clip_br.y))
- {
emitLineTo(line_to_pos.x, line_to_pos.y);
+ } else {
+ hasMoveTo = false;
}
+ move_to_pos = line_to_pos;
} else {
move_to_pos.x = qt_real_to_fixed(p2.x());
move_to_pos.y = qt_real_to_fixed(p2.y());
}
-
- idash = (idash + idash_incr) % dashCount;
}
// Shuffle to the next cycle...