118 }; |
118 }; |
119 |
119 |
120 class QSubpathFlatIterator |
120 class QSubpathFlatIterator |
121 { |
121 { |
122 public: |
122 public: |
123 QSubpathFlatIterator(const QDataBuffer<QStrokerOps::Element> *path) |
123 QSubpathFlatIterator(const QDataBuffer<QStrokerOps::Element> *path, qreal threshold) |
124 : m_path(path), m_pos(0), m_curve_index(-1) { } |
124 : m_path(path), m_pos(0), m_curve_index(-1), m_curve_threshold(threshold) { } |
125 |
125 |
126 inline bool hasNext() const { return m_curve_index >= 0 || m_pos < m_path->size(); } |
126 inline bool hasNext() const { return m_curve_index >= 0 || m_pos < m_path->size(); } |
127 |
127 |
128 QStrokerOps::Element next() |
128 QStrokerOps::Element next() |
129 { |
129 { |
150 QPointF(qt_fixed_to_real(e.x), |
150 QPointF(qt_fixed_to_real(e.x), |
151 qt_fixed_to_real(e.y)), |
151 qt_fixed_to_real(e.y)), |
152 QPointF(qt_fixed_to_real(m_path->at(m_pos+1).x), |
152 QPointF(qt_fixed_to_real(m_path->at(m_pos+1).x), |
153 qt_fixed_to_real(m_path->at(m_pos+1).y)), |
153 qt_fixed_to_real(m_path->at(m_pos+1).y)), |
154 QPointF(qt_fixed_to_real(m_path->at(m_pos+2).x), |
154 QPointF(qt_fixed_to_real(m_path->at(m_pos+2).x), |
155 qt_fixed_to_real(m_path->at(m_pos+2).y))).toPolygon(); |
155 qt_fixed_to_real(m_path->at(m_pos+2).y))).toPolygon(m_curve_threshold); |
156 m_curve_index = 1; |
156 m_curve_index = 1; |
157 e.type = QPainterPath::LineToElement; |
157 e.type = QPainterPath::LineToElement; |
158 e.x = m_curve.at(0).x(); |
158 e.x = m_curve.at(0).x(); |
159 e.y = m_curve.at(0).y(); |
159 e.y = m_curve.at(0).y(); |
160 m_pos += 2; |
160 m_pos += 2; |
167 private: |
167 private: |
168 const QDataBuffer<QStrokerOps::Element> *m_path; |
168 const QDataBuffer<QStrokerOps::Element> *m_path; |
169 int m_pos; |
169 int m_pos; |
170 QPolygonF m_curve; |
170 QPolygonF m_curve; |
171 int m_curve_index; |
171 int m_curve_index; |
|
172 qreal m_curve_threshold; |
172 }; |
173 }; |
173 |
174 |
174 template <class Iterator> bool qt_stroke_side(Iterator *it, QStroker *stroker, |
175 template <class Iterator> bool qt_stroke_side(Iterator *it, QStroker *stroker, |
175 bool capFirst, QLineF *startTangent); |
176 bool capFirst, QLineF *startTangent); |
176 |
177 |
185 angle = 360 - angle; |
186 angle = 360 - angle; |
186 return angle; |
187 return angle; |
187 } |
188 } |
188 |
189 |
189 QStrokerOps::QStrokerOps() |
190 QStrokerOps::QStrokerOps() |
190 : m_customData(0), m_moveTo(0), m_lineTo(0), m_cubicTo(0) |
191 : m_elements(0) |
|
192 , m_curveThreshold(qt_real_to_fixed(0.25)) |
|
193 , m_customData(0) |
|
194 , m_moveTo(0) |
|
195 , m_lineTo(0) |
|
196 , m_cubicTo(0) |
191 { |
197 { |
192 } |
198 } |
193 |
199 |
194 QStrokerOps::~QStrokerOps() |
200 QStrokerOps::~QStrokerOps() |
195 { |
201 { |
196 } |
202 } |
197 |
|
198 |
203 |
199 /*! |
204 /*! |
200 Prepares the stroker. Call this function once before starting a |
205 Prepares the stroker. Call this function once before starting a |
201 stroke by calling moveTo, lineTo or cubicTo. |
206 stroke by calling moveTo, lineTo or cubicTo. |
202 |
207 |
236 void QStrokerOps::strokePath(const QPainterPath &path, void *customData, const QTransform &matrix) |
241 void QStrokerOps::strokePath(const QPainterPath &path, void *customData, const QTransform &matrix) |
237 { |
242 { |
238 if (path.isEmpty()) |
243 if (path.isEmpty()) |
239 return; |
244 return; |
240 |
245 |
|
246 setCurveThresholdFromTransform(matrix); |
241 begin(customData); |
247 begin(customData); |
242 int count = path.elementCount(); |
248 int count = path.elementCount(); |
243 if (matrix.isIdentity()) { |
249 if (matrix.isIdentity()) { |
244 for (int i=0; i<count; ++i) { |
250 for (int i=0; i<count; ++i) { |
245 const QPainterPath::Element &e = path.elementAt(i); |
251 const QPainterPath::Element &e = path.elementAt(i); |
306 void QStrokerOps::strokePolygon(const QPointF *points, int pointCount, bool implicit_close, |
312 void QStrokerOps::strokePolygon(const QPointF *points, int pointCount, bool implicit_close, |
307 void *data, const QTransform &matrix) |
313 void *data, const QTransform &matrix) |
308 { |
314 { |
309 if (!pointCount) |
315 if (!pointCount) |
310 return; |
316 return; |
|
317 |
|
318 setCurveThresholdFromTransform(matrix); |
311 begin(data); |
319 begin(data); |
312 if (matrix.isIdentity()) { |
320 if (matrix.isIdentity()) { |
313 moveTo(qt_real_to_fixed(points[0].x()), qt_real_to_fixed(points[0].y())); |
321 moveTo(qt_real_to_fixed(points[0].x()), qt_real_to_fixed(points[0].y())); |
314 for (int i=1; i<pointCount; ++i) |
322 for (int i=1; i<pointCount; ++i) |
315 lineTo(qt_real_to_fixed(points[i].x()), |
323 lineTo(qt_real_to_fixed(points[i].x()), |
346 for (int i=0; i<12; ++i) { |
354 for (int i=0; i<12; ++i) { |
347 pts[i] = pts[i] * matrix; |
355 pts[i] = pts[i] * matrix; |
348 } |
356 } |
349 } |
357 } |
350 |
358 |
|
359 setCurveThresholdFromTransform(matrix); |
351 begin(data); |
360 begin(data); |
352 moveTo(qt_real_to_fixed(start.x()), qt_real_to_fixed(start.y())); |
361 moveTo(qt_real_to_fixed(start.x()), qt_real_to_fixed(start.y())); |
353 for (int i=0; i<12; i+=3) { |
362 for (int i=0; i<12; i+=3) { |
354 cubicTo(qt_real_to_fixed(pts[i].x()), qt_real_to_fixed(pts[i].y()), |
363 cubicTo(qt_real_to_fixed(pts[i].x()), qt_real_to_fixed(pts[i].y()), |
355 qt_real_to_fixed(pts[i+1].x()), qt_real_to_fixed(pts[i+1].y()), |
364 qt_real_to_fixed(pts[i+1].x()), qt_real_to_fixed(pts[i+1].y()), |
364 m_back1X(0), m_back1Y(0), |
373 m_back1X(0), m_back1Y(0), |
365 m_back2X(0), m_back2Y(0) |
374 m_back2X(0), m_back2Y(0) |
366 { |
375 { |
367 m_strokeWidth = qt_real_to_fixed(1); |
376 m_strokeWidth = qt_real_to_fixed(1); |
368 m_miterLimit = qt_real_to_fixed(2); |
377 m_miterLimit = qt_real_to_fixed(2); |
369 m_curveThreshold = qt_real_to_fixed(0.25); |
|
370 } |
378 } |
371 |
379 |
372 QStroker::~QStroker() |
380 QStroker::~QStroker() |
373 { |
381 { |
374 |
|
375 } |
382 } |
376 |
383 |
377 Qt::PenCapStyle QStroker::capForJoinMode(LineJoinMode mode) |
384 Qt::PenCapStyle QStroker::capForJoinMode(LineJoinMode mode) |
378 { |
385 { |
379 if (mode == FlatJoin) return Qt::FlatCap; |
386 if (mode == FlatJoin) return Qt::FlatCap; |
1041 } |
1048 } |
1042 |
1049 |
1043 return pattern; |
1050 return pattern; |
1044 } |
1051 } |
1045 |
1052 |
|
1053 static inline bool lineRectIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br) |
|
1054 { |
|
1055 return ((p1.x > tl.x || p2.x > tl.x) && (p1.x < br.x || p2.x < br.x) |
|
1056 && (p1.y > tl.y || p2.y > tl.y) && (p1.y < br.y || p2.y < br.y)); |
|
1057 } |
|
1058 |
|
1059 // If the line intersects the rectangle, this function will return true. |
|
1060 static bool lineIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br) |
|
1061 { |
|
1062 if (!lineRectIntersectsRect(p1, p2, tl, br)) |
|
1063 return false; |
|
1064 if (p1.x == p2.x || p1.y == p2.y) |
|
1065 return true; |
|
1066 |
|
1067 if (p1.y > p2.y) |
|
1068 qSwap(p1, p2); // make p1 above p2 |
|
1069 qfixed2d u; |
|
1070 qfixed2d v; |
|
1071 qfixed2d w = {p2.x - p1.x, p2.y - p1.y}; |
|
1072 if (p1.x < p2.x) { |
|
1073 // backslash |
|
1074 u.x = tl.x - p1.x; u.y = br.y - p1.y; |
|
1075 v.x = br.x - p1.x; v.y = tl.y - p1.y; |
|
1076 } else { |
|
1077 // slash |
|
1078 u.x = tl.x - p1.x; u.y = tl.y - p1.y; |
|
1079 v.x = br.x - p1.x; v.y = br.y - p1.y; |
|
1080 } |
|
1081 #if defined(QFIXED_IS_26_6) || defined(QFIXED_IS_16_16) |
|
1082 qint64 val1 = qint64(u.x) * qint64(w.y) - qint64(u.y) * qint64(w.x); |
|
1083 qint64 val2 = qint64(v.x) * qint64(w.y) - qint64(v.y) * qint64(w.x); |
|
1084 return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0); |
|
1085 #elif defined(QFIXED_IS_32_32) |
|
1086 // Cannot do proper test because it may overflow. |
|
1087 return true; |
|
1088 #else |
|
1089 qreal val1 = u.x * w.y - u.y * w.x; |
|
1090 qreal val2 = v.x * w.y - v.y * w.x; |
|
1091 return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0); |
|
1092 #endif |
|
1093 } |
1046 |
1094 |
1047 void QDashStroker::processCurrentSubpath() |
1095 void QDashStroker::processCurrentSubpath() |
1048 { |
1096 { |
1049 int dashCount = qMin(m_dashPattern.size(), 32); |
1097 int dashCount = qMin(m_dashPattern.size(), 32); |
1050 qfixed dashes[32]; |
1098 qfixed dashes[32]; |
1065 } |
1113 } |
1066 |
1114 |
1067 if (qFuzzyIsNull(sumLength)) |
1115 if (qFuzzyIsNull(sumLength)) |
1068 return; |
1116 return; |
1069 |
1117 |
|
1118 qreal invSumLength = qreal(1) / sumLength; |
|
1119 |
1070 Q_ASSERT(dashCount > 0); |
1120 Q_ASSERT(dashCount > 0); |
1071 |
1121 |
1072 dashCount = (dashCount / 2) * 2; // Round down to even number |
1122 dashCount = dashCount & -2; // Round down to even number |
1073 |
1123 |
1074 int idash = 0; // Index to current dash |
1124 int idash = 0; // Index to current dash |
1075 qreal pos = 0; // The position on the curve, 0 <= pos <= path.length |
1125 qreal pos = 0; // The position on the curve, 0 <= pos <= path.length |
1076 qreal elen = 0; // element length |
1126 qreal elen = 0; // element length |
1077 qreal doffset = m_dashOffset * m_stroke_width; |
1127 qreal doffset = m_dashOffset * m_stroke_width; |
1078 |
1128 |
1079 // make sure doffset is in range [0..sumLength) |
1129 // make sure doffset is in range [0..sumLength) |
1080 doffset -= qFloor(doffset / sumLength) * sumLength; |
1130 doffset -= qFloor(doffset * invSumLength) * sumLength; |
1081 |
1131 |
1082 while (doffset >= dashes[idash]) { |
1132 while (doffset >= dashes[idash]) { |
1083 doffset -= dashes[idash]; |
1133 doffset -= dashes[idash]; |
1084 idash = (idash + 1) % dashCount; |
1134 if (++idash >= dashCount) |
|
1135 idash = 0; |
1085 } |
1136 } |
1086 |
1137 |
1087 qreal estart = 0; // The elements starting position |
1138 qreal estart = 0; // The elements starting position |
1088 qreal estop = 0; // The element stop position |
1139 qreal estop = 0; // The element stop position |
1089 |
1140 |
1090 QLineF cline; |
1141 QLineF cline; |
1091 |
1142 |
1092 QPainterPath dashPath; |
1143 QPainterPath dashPath; |
1093 |
1144 |
1094 QSubpathFlatIterator it(&m_elements); |
1145 QSubpathFlatIterator it(&m_elements, m_curveThreshold); |
1095 qfixed2d prev = it.next(); |
1146 qfixed2d prev = it.next(); |
1096 |
1147 |
1097 bool clipping = !m_clip_rect.isEmpty(); |
1148 bool clipping = !m_clip_rect.isEmpty(); |
1098 qfixed2d move_to_pos = prev; |
1149 qfixed2d move_to_pos = prev; |
1099 qfixed2d line_to_pos; |
1150 qfixed2d line_to_pos; |
1117 elen = cline.length(); |
1168 elen = cline.length(); |
1118 |
1169 |
1119 estop = estart + elen; |
1170 estop = estart + elen; |
1120 |
1171 |
1121 bool done = pos >= estop; |
1172 bool done = pos >= estop; |
|
1173 |
|
1174 if (clipping) { |
|
1175 // Check if the entire line can be clipped away. |
|
1176 if (!lineIntersectsRect(prev, e, clip_tl, clip_br)) { |
|
1177 // Cut away full dash sequences. |
|
1178 elen -= qFloor(elen * invSumLength) * sumLength; |
|
1179 // Update dash offset. |
|
1180 while (!done) { |
|
1181 qreal dpos = pos + dashes[idash] - doffset - estart; |
|
1182 |
|
1183 Q_ASSERT(dpos >= 0); |
|
1184 |
|
1185 if (dpos > elen) { // dash extends this line |
|
1186 doffset = dashes[idash] - (dpos - elen); // subtract the part already used |
|
1187 pos = estop; // move pos to next path element |
|
1188 done = true; |
|
1189 } else { // Dash is on this line |
|
1190 pos = dpos + estart; |
|
1191 done = pos >= estop; |
|
1192 if (++idash >= dashCount) |
|
1193 idash = 0; |
|
1194 doffset = 0; // full segment so no offset on next. |
|
1195 } |
|
1196 } |
|
1197 hasMoveTo = false; |
|
1198 move_to_pos = e; |
|
1199 } |
|
1200 } |
|
1201 |
1122 // Dash away... |
1202 // Dash away... |
1123 while (!done) { |
1203 while (!done) { |
1124 QPointF p2; |
1204 QPointF p2; |
1125 |
1205 |
1126 int idash_incr = 0; |
|
1127 bool has_offset = doffset > 0; |
1206 bool has_offset = doffset > 0; |
|
1207 bool evenDash = (idash & 1) == 0; |
1128 qreal dpos = pos + dashes[idash] - doffset - estart; |
1208 qreal dpos = pos + dashes[idash] - doffset - estart; |
1129 |
1209 |
1130 Q_ASSERT(dpos >= 0); |
1210 Q_ASSERT(dpos >= 0); |
1131 |
1211 |
1132 if (dpos > elen) { // dash extends this line |
1212 if (dpos > elen) { // dash extends this line |
1136 p2 = cline.p2(); |
1216 p2 = cline.p2(); |
1137 } else { // Dash is on this line |
1217 } else { // Dash is on this line |
1138 p2 = cline.pointAt(dpos/elen); |
1218 p2 = cline.pointAt(dpos/elen); |
1139 pos = dpos + estart; |
1219 pos = dpos + estart; |
1140 done = pos >= estop; |
1220 done = pos >= estop; |
1141 idash_incr = 1; |
1221 if (++idash >= dashCount) |
|
1222 idash = 0; |
1142 doffset = 0; // full segment so no offset on next. |
1223 doffset = 0; // full segment so no offset on next. |
1143 } |
1224 } |
1144 |
1225 |
1145 if (idash % 2 == 0) { |
1226 if (evenDash) { |
1146 line_to_pos.x = qt_real_to_fixed(p2.x()); |
1227 line_to_pos.x = qt_real_to_fixed(p2.x()); |
1147 line_to_pos.y = qt_real_to_fixed(p2.y()); |
1228 line_to_pos.y = qt_real_to_fixed(p2.y()); |
1148 |
1229 |
1149 // If we have an offset, we're continuing a dash |
1230 if (!clipping |
1150 // from a previous element and should only |
1231 || lineRectIntersectsRect(move_to_pos, line_to_pos, clip_tl, clip_br)) |
1151 // continue the current dash, without starting a |
1232 { |
1152 // new subpath. |
1233 // If we have an offset, we're continuing a dash |
1153 if (!has_offset || !hasMoveTo) { |
1234 // from a previous element and should only |
1154 emitMoveTo(move_to_pos.x, move_to_pos.y); |
1235 // continue the current dash, without starting a |
1155 hasMoveTo = true; |
1236 // new subpath. |
|
1237 if (!has_offset || !hasMoveTo) { |
|
1238 emitMoveTo(move_to_pos.x, move_to_pos.y); |
|
1239 hasMoveTo = true; |
|
1240 } |
|
1241 |
|
1242 emitLineTo(line_to_pos.x, line_to_pos.y); |
|
1243 } else { |
|
1244 hasMoveTo = false; |
1156 } |
1245 } |
1157 |
1246 move_to_pos = line_to_pos; |
1158 if (!clipping |
|
1159 // if move_to is inside... |
|
1160 || (move_to_pos.x > clip_tl.x && move_to_pos.x < clip_br.x |
|
1161 && move_to_pos.y > clip_tl.y && move_to_pos.y < clip_br.y) |
|
1162 // Or if line_to is inside... |
|
1163 || (line_to_pos.x > clip_tl.x && line_to_pos.x < clip_br.x |
|
1164 && line_to_pos.y > clip_tl.y && line_to_pos.y < clip_br.y)) |
|
1165 { |
|
1166 emitLineTo(line_to_pos.x, line_to_pos.y); |
|
1167 } |
|
1168 } else { |
1247 } else { |
1169 move_to_pos.x = qt_real_to_fixed(p2.x()); |
1248 move_to_pos.x = qt_real_to_fixed(p2.x()); |
1170 move_to_pos.y = qt_real_to_fixed(p2.y()); |
1249 move_to_pos.y = qt_real_to_fixed(p2.y()); |
1171 } |
1250 } |
1172 |
|
1173 idash = (idash + idash_incr) % dashCount; |
|
1174 } |
1251 } |
1175 |
1252 |
1176 // Shuffle to the next cycle... |
1253 // Shuffle to the next cycle... |
1177 estart = estop; |
1254 estart = estop; |
1178 prev = e; |
1255 prev = e; |