src/gui/painting/qstroker.cpp
branchGCC_SURGE
changeset 31 5daf16870df6
parent 30 5dc02b23752f
equal deleted inserted replaced
27:93b982ccede2 31:5daf16870df6
   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;