QtBase  v6.3.1
qstroker.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtGui module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39 
40 #include "private/qstroker_p.h"
41 #include "private/qbezier_p.h"
42 #include "qline.h"
43 #include "qtransform.h"
44 #include <qmath.h>
45 
47 
48 // #define QPP_STROKE_DEBUG
49 
51 {
52 public:
54  : m_path(path), m_pos(0) { }
55  inline int position() const { return m_pos; }
56  inline bool hasNext() const { return m_pos < m_path->size(); }
57  inline QStrokerOps::Element next() { Q_ASSERT(hasNext()); return m_path->at(m_pos++); }
58 
59 private:
61  int m_pos;
62 };
63 
65 {
66 public:
68  : m_path(path), m_pos(path->size() - 1) { }
69 
70  inline int position() const { return m_pos; }
71 
72  inline bool hasNext() const { return m_pos >= 0; }
73 
75  {
76  Q_ASSERT(hasNext());
77 
78  QStrokerOps::Element ce = m_path->at(m_pos); // current element
79 
80  if (m_pos == m_path->size() - 1) {
81  --m_pos;
83  return ce;
84  }
85 
86  const QStrokerOps::Element &pe = m_path->at(m_pos + 1); // previous element
87 
88  switch (pe.type) {
91  break;
93  // First control point?
96  } else { // Second control point then
98  }
99  break;
102  break;
103  default:
104  qWarning("QSubpathReverseIterator::next: Case %d unhandled", ce.type);
105  break;
106  }
107  --m_pos;
108 
109  return ce;
110  }
111 
112 private:
113  const QDataBuffer<QStrokerOps::Element> *m_path;
114  int m_pos;
115 };
116 
118 {
119 public:
121  : m_path(path), m_pos(0), m_curve_index(-1), m_curve_threshold(threshold) { }
122 
123  inline bool hasNext() const { return m_curve_index >= 0 || m_pos < m_path->size(); }
124 
126  {
127  Q_ASSERT(hasNext());
128 
129  if (m_curve_index >= 0) {
131  qt_real_to_fixed(m_curve.at(m_curve_index).x()),
132  qt_real_to_fixed(m_curve.at(m_curve_index).y())
133  };
134  ++m_curve_index;
135  if (m_curve_index >= m_curve.size())
136  m_curve_index = -1;
137  return e;
138  }
139 
140  QStrokerOps::Element e = m_path->at(m_pos);
141  if (e.isCurveTo()) {
142  Q_ASSERT(m_pos > 0);
143  Q_ASSERT(m_pos < m_path->size());
144 
145  m_curve = QBezier::fromPoints(QPointF(qt_fixed_to_real(m_path->at(m_pos-1).x),
146  qt_fixed_to_real(m_path->at(m_pos-1).y)),
148  qt_fixed_to_real(e.y)),
149  QPointF(qt_fixed_to_real(m_path->at(m_pos+1).x),
150  qt_fixed_to_real(m_path->at(m_pos+1).y)),
151  QPointF(qt_fixed_to_real(m_path->at(m_pos+2).x),
152  qt_fixed_to_real(m_path->at(m_pos+2).y))).toPolygon(m_curve_threshold);
153  m_curve_index = 1;
155  e.x = m_curve.at(0).x();
156  e.y = m_curve.at(0).y();
157  m_pos += 2;
158  }
159  Q_ASSERT(e.isLineTo() || e.isMoveTo());
160  ++m_pos;
161  return e;
162  }
163 
164 private:
165  const QDataBuffer<QStrokerOps::Element> *m_path;
166  int m_pos;
167  QPolygonF m_curve;
168  int m_curve_index;
169  qreal m_curve_threshold;
170 };
171 
172 template <class Iterator> bool qt_stroke_side(Iterator *it, QStroker *stroker,
173  bool capFirst, QLineF *startTangent);
174 
175 /*******************************************************************************
176  * QLineF::angleTo gives us the angle between two lines with respecting the direction.
177  * Here we want to identify the line's angle direction on the unit circle.
178  */
179 static inline qreal adapted_angle_on_x(const QLineF &line)
180 {
181  return QLineF(0, 0, 1, 0).angleTo(line);
182 }
183 
185  : m_elements(0)
186  , m_curveThreshold(qt_real_to_fixed(0.25))
187  , m_dashThreshold(qt_real_to_fixed(0.25))
188  , m_customData(nullptr)
189  , m_moveTo(nullptr)
190  , m_lineTo(nullptr)
191  , m_cubicTo(nullptr)
192 {
193 }
194 
196 {
197 }
198 
207 void QStrokerOps::begin(void *customData)
208 {
209  m_customData = customData;
210  m_elements.reset();
211 }
212 
213 
219 {
220  if (m_elements.size() > 1)
222  m_customData = nullptr;
223 }
224 
236 void QStrokerOps::strokePath(const QPainterPath &path, void *customData, const QTransform &matrix)
237 {
238  if (path.isEmpty())
239  return;
240 
242  begin(customData);
243  int count = path.elementCount();
244  if (matrix.isIdentity()) {
245  for (int i=0; i<count; ++i) {
246  const QPainterPath::Element &e = path.elementAt(i);
247  switch (e.type) {
250  break;
253  break;
255  {
256  const QPainterPath::Element &cp2 = path.elementAt(++i);
257  const QPainterPath::Element &ep = path.elementAt(++i);
261  }
262  break;
263  default:
264  break;
265  }
266  }
267  } else {
268  for (int i=0; i<count; ++i) {
269  const QPainterPath::Element &e = path.elementAt(i);
270  QPointF pt = QPointF(e.x, e.y) * matrix;
271  switch (e.type) {
274  break;
277  break;
279  {
280  QPointF cp2 = ((QPointF) path.elementAt(++i)) * matrix;
281  QPointF ep = ((QPointF) path.elementAt(++i)) * matrix;
283  qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
284  qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
285  }
286  break;
287  default:
288  break;
289  }
290  }
291  }
292  end();
293 }
294 
307 void QStrokerOps::strokePolygon(const QPointF *points, int pointCount, bool implicit_close,
308  void *data, const QTransform &matrix)
309 {
310  if (!pointCount)
311  return;
312 
314  begin(data);
315  if (matrix.isIdentity()) {
317  for (int i=1; i<pointCount; ++i)
319  qt_real_to_fixed(points[i].y()));
320  if (implicit_close)
322  } else {
323  QPointF start = points[0] * matrix;
325  for (int i=1; i<pointCount; ++i) {
326  QPointF pt = points[i] * matrix;
328  }
329  if (implicit_close)
331  }
332  end();
333 }
334 
341 {
342  int count = 0;
343  QPointF pts[12];
344  QPointF start = qt_curves_for_arc(rect, 0, -360, pts, &count);
345  Q_ASSERT(count == 12); // a perfect circle..
346 
347  if (!matrix.isIdentity()) {
348  start = start * matrix;
349  for (int i=0; i<12; ++i) {
350  pts[i] = pts[i] * matrix;
351  }
352  }
353 
355  begin(data);
357  for (int i=0; i<12; i+=3) {
358  cubicTo(qt_real_to_fixed(pts[i].x()), qt_real_to_fixed(pts[i].y()),
359  qt_real_to_fixed(pts[i+1].x()), qt_real_to_fixed(pts[i+1].y()),
360  qt_real_to_fixed(pts[i+2].x()), qt_real_to_fixed(pts[i+2].y()));
361  }
362  end();
363 }
364 
365 
367  : m_capStyle(SquareJoin), m_joinStyle(FlatJoin),
368  m_back1X(0), m_back1Y(0),
369  m_back2X(0), m_back2Y(0),
370  m_forceOpen(false)
371 {
374 }
375 
377 {
378 }
379 
381 {
382  if (mode == FlatJoin) return Qt::FlatCap;
383  else if (mode == SquareJoin) return Qt::SquareCap;
384  else return Qt::RoundCap;
385 }
386 
388 {
389  if (style == Qt::FlatCap) return FlatJoin;
390  else if (style == Qt::SquareCap) return SquareJoin;
391  else return RoundCap;
392 }
393 
395 {
396  if (mode == FlatJoin) return Qt::BevelJoin;
397  else if (mode == MiterJoin) return Qt::MiterJoin;
398  else if (mode == SvgMiterJoin) return Qt::SvgMiterJoin;
399  else return Qt::RoundJoin;
400 }
401 
403 {
404  if (joinStyle == Qt::BevelJoin) return FlatJoin;
405  else if (joinStyle == Qt::MiterJoin) return MiterJoin;
406  else if (joinStyle == Qt::SvgMiterJoin) return SvgMiterJoin;
407  else return RoundJoin;
408 }
409 
410 
416 {
417  Q_ASSERT(!m_elements.isEmpty());
419  Q_ASSERT(m_elements.size() > 1);
420 
423 
424  QLineF fwStartTangent, bwStartTangent;
425 
426  bool fwclosed = qt_stroke_side(&fwit, this, false, &fwStartTangent);
427  bool bwclosed = qt_stroke_side(&bwit, this, !fwclosed, &bwStartTangent);
428 
429  if (!bwclosed && !fwStartTangent.isNull())
430  joinPoints(m_elements.at(0).x, m_elements.at(0).y, fwStartTangent, m_capStyle);
431 }
432 
433 
437 void QStroker::joinPoints(qfixed focal_x, qfixed focal_y, const QLineF &nextLine, LineJoinMode join)
438 {
439 #ifdef QPP_STROKE_DEBUG
440  printf(" -----> joinPoints: around=(%.0f, %.0f), next_p1=(%.0f, %.f) next_p2=(%.0f, %.f)\n",
441  qt_fixed_to_real(focal_x),
442  qt_fixed_to_real(focal_y),
443  nextLine.x1(), nextLine.y1(), nextLine.x2(), nextLine.y2());
444 #endif
445  // points connected already, don't join
446 
447 #if !defined (QFIXED_26_6) && !defined (Q_FIXED_32_32)
448  if (qFuzzyCompare(m_back1X, nextLine.x1()) && qFuzzyCompare(m_back1Y, nextLine.y1()))
449  return;
450 #else
451  if (m_back1X == qt_real_to_fixed(nextLine.x1())
452  && m_back1Y == qt_real_to_fixed(nextLine.y1())) {
453  return;
454  }
455 #endif
458  QPointF isect;
459  QLineF::IntersectionType type = prevLine.intersects(nextLine, &isect);
460 
461  if (join == FlatJoin) {
462  QLineF shortCut(prevLine.p2(), nextLine.p1());
463  qreal angle = shortCut.angleTo(prevLine);
464  if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
465  emitLineTo(focal_x, focal_y);
466  emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
467  return;
468  }
469  emitLineTo(qt_real_to_fixed(nextLine.x1()),
470  qt_real_to_fixed(nextLine.y1()));
471 
472  } else {
473  if (join == MiterJoin) {
474  qreal appliedMiterLimit = qt_fixed_to_real(m_strokeWidth * m_miterLimit);
475 
476  // If we are on the inside, do the short cut...
477  QLineF shortCut(prevLine.p2(), nextLine.p1());
478  qreal angle = shortCut.angleTo(prevLine);
479  if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
480  emitLineTo(focal_x, focal_y);
481  emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
482  return;
483  }
485  qt_fixed_to_real(m_back1Y)), isect);
486  if (type == QLineF::NoIntersection || miterLine.length() > appliedMiterLimit) {
487  QLineF l1(prevLine);
488  l1.setLength(appliedMiterLimit);
489  l1.translate(prevLine.dx(), prevLine.dy());
490 
491  QLineF l2(nextLine);
492  l2.setLength(appliedMiterLimit);
493  l2.translate(-l2.dx(), -l2.dy());
494 
497  emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
498  } else {
499  emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
500  emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
501  }
502 
503  } else if (join == SquareJoin) {
505 
506  QLineF l1(prevLine);
507  qreal dp = QPointF::dotProduct(QPointF(prevLine.dx(), prevLine.dy()), QPointF(nextLine.dx(), nextLine.dy()));
508  if (dp > 0) // same direction, means that prevLine is from a bezier that has been "reversed" by shifting
509  l1 = QLineF(prevLine.p2(), prevLine.p1());
510  else
511  l1.translate(l1.dx(), l1.dy());
513  QLineF l2(nextLine.p2(), nextLine.p1());
514  l2.translate(l2.dx(), l2.dy());
519 
520  } else if (join == RoundJoin) {
522 
523  QLineF shortCut(prevLine.p2(), nextLine.p1());
524  qreal angle = shortCut.angleTo(prevLine);
525  if ((type == QLineF::BoundedIntersection || (angle > qreal(90.01))) && nextLine.length() > offset) {
526  emitLineTo(focal_x, focal_y);
527  emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
528  return;
529  }
530  qreal l1_on_x = adapted_angle_on_x(prevLine);
531  qreal l2_on_x = adapted_angle_on_x(nextLine);
532 
533  qreal sweepLength = qAbs(l2_on_x - l1_on_x);
534 
535  int point_count;
536  QPointF curves[15];
537 
538  QPointF curve_start =
540  qt_fixed_to_real(focal_y - offset),
542  qt_fixed_to_real(offset * 2)),
543  l1_on_x + 90, -sweepLength,
544  curves, &point_count);
545 
546 // // line to the beginning of the arc segment, (should not be needed).
547 // emitLineTo(qt_real_to_fixed(curve_start.x()), qt_real_to_fixed(curve_start.y()));
548  Q_UNUSED(curve_start);
549 
550  for (int i=0; i<point_count; i+=3) {
551  emitCubicTo(qt_real_to_fixed(curves[i].x()),
552  qt_real_to_fixed(curves[i].y()),
553  qt_real_to_fixed(curves[i+1].x()),
554  qt_real_to_fixed(curves[i+1].y()),
555  qt_real_to_fixed(curves[i+2].x()),
556  qt_real_to_fixed(curves[i+2].y()));
557  }
558 
559  // line to the end of the arc segment, (should also not be needed).
560  emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
561 
562  // Same as round join except we know its 180 degrees. Can also optimize this
563  // later based on the addEllipse logic
564  } else if (join == RoundCap) {
566 
567  // first control line
568  QLineF l1 = prevLine;
569  qreal dp = QPointF::dotProduct(QPointF(prevLine.dx(), prevLine.dy()), QPointF(nextLine.dx(), nextLine.dy()));
570  if (dp > 0) // same direction, means that prevLine is from a bezier that has been "reversed" by shifting
571  l1 = QLineF(prevLine.p2(), prevLine.p1());
572  else
573  l1.translate(l1.dx(), l1.dy());
575 
576  // second control line, find through normal between prevLine and focal.
577  QLineF l2(qt_fixed_to_real(focal_x), qt_fixed_to_real(focal_y),
578  prevLine.x2(), prevLine.y2());
579  l2.translate(-l2.dy(), l2.dx());
581 
583  qt_real_to_fixed(l1.y2()),
584  qt_real_to_fixed(l2.x2()),
585  qt_real_to_fixed(l2.y2()),
586  qt_real_to_fixed(l2.x1()),
587  qt_real_to_fixed(l2.y1()));
588 
589  // move so that it matches
590  l2 = QLineF(l2.x1(), l2.y1(), l2.x1()-l2.dx(), l2.y1()-l2.dy());
591 
592  // last line is parallel to l1 so just shift it down.
593  l1.translate(nextLine.x1() - l1.x1(), nextLine.y1() - l1.y1());
594 
596  qt_real_to_fixed(l2.y2()),
597  qt_real_to_fixed(l1.x2()),
598  qt_real_to_fixed(l1.y2()),
599  qt_real_to_fixed(l1.x1()),
600  qt_real_to_fixed(l1.y1()));
601  } else if (join == SvgMiterJoin) {
602  QLineF shortCut(prevLine.p2(), nextLine.p1());
603  qreal angle = shortCut.angleTo(prevLine);
604  if (type == QLineF::BoundedIntersection || (angle > 90 && !qFuzzyCompare(angle, (qreal)90))) {
605  emitLineTo(focal_x, focal_y);
606  emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
607  return;
608  }
609  QLineF miterLine(QPointF(qt_fixed_to_real(focal_x),
610  qt_fixed_to_real(focal_y)), isect);
612  emitLineTo(qt_real_to_fixed(nextLine.x1()),
613  qt_real_to_fixed(nextLine.y1()));
614  } else {
615  emitLineTo(qt_real_to_fixed(isect.x()), qt_real_to_fixed(isect.y()));
616  emitLineTo(qt_real_to_fixed(nextLine.x1()), qt_real_to_fixed(nextLine.y1()));
617  }
618  } else {
619  Q_ASSERT(!"QStroker::joinPoints(), bad join style...");
620  }
621  }
622 }
623 
624 
625 /*
626  Strokes a subpath side using the \a it as source. Results are put into
627  \a stroke. The function returns \c true if the subpath side was closed.
628  If \a capFirst is true, we will use capPoints instead of joinPoints to
629  connect the first segment, other segments will be joined using joinPoints.
630  This is to put capping in order...
631 */
632 template <class Iterator> bool qt_stroke_side(Iterator *it,
633  QStroker *stroker,
634  bool capFirst,
635  QLineF *startTangent)
636 {
637  // Used in CurveToElement section below.
638  const int MAX_OFFSET = 16;
639  QBezier offsetCurves[MAX_OFFSET];
640 
641  Q_ASSERT(it->hasNext()); // The initaial move to
642  QStrokerOps::Element first_element = it->next();
643  Q_ASSERT(first_element.isMoveTo());
644 
645  qfixed2d start = first_element;
646 
647 #ifdef QPP_STROKE_DEBUG
648  qDebug(" -> (side) [%.2f, %.2f], startPos=%d",
651 #endif
652 
653  qfixed2d prev = start;
654 
655  bool first = true;
656 
657  qfixed offset = stroker->strokeWidth() / 2;
658 
659  while (it->hasNext()) {
660  QStrokerOps::Element e = it->next();
661 
662  // LineToElement
663  if (e.isLineTo()) {
664 #ifdef QPP_STROKE_DEBUG
665  qDebug("\n ---> (side) lineto [%.2f, %.2f]", e.x, e.y);
666 #endif
669  if (line.p1() != line.p2()) {
670  QLineF normal = line.normalVector();
671  normal.setLength(offset);
672  line.translate(normal.dx(), normal.dy());
673 
674  // If we are starting a new subpath, move to correct starting point.
675  if (first) {
676  if (capFirst)
677  stroker->joinPoints(prev.x, prev.y, line, stroker->capStyleMode());
678  else
679  stroker->emitMoveTo(qt_real_to_fixed(line.x1()), qt_real_to_fixed(line.y1()));
680  *startTangent = line;
681  first = false;
682  } else {
683  stroker->joinPoints(prev.x, prev.y, line, stroker->joinStyleMode());
684  }
685 
686  // Add the stroke for this line.
687  stroker->emitLineTo(qt_real_to_fixed(line.x2()),
688  qt_real_to_fixed(line.y2()));
689  prev = e;
690  }
691 
692  // CurveToElement
693  } else if (e.isCurveTo()) {
694  QStrokerOps::Element cp2 = it->next(); // control point 2
695  QStrokerOps::Element ep = it->next(); // end point
696 
697 #ifdef QPP_STROKE_DEBUG
698  qDebug("\n ---> (side) cubicTo [%.2f, %.2f]",
699  qt_fixed_to_real(ep.x),
700  qt_fixed_to_real(ep.y));
701 #endif
702 
703  QBezier bezier =
708  int count = bezier.shifted(offsetCurves,
709  MAX_OFFSET,
710  offset,
711  stroker->curveThreshold());
712 
713  if (count) {
714  // If we are starting a new subpath, move to correct starting point
715  QLineF tangent = bezier.startTangent();
716  tangent.translate(offsetCurves[0].pt1() - bezier.pt1());
717  if (first) {
718  QPointF pt = offsetCurves[0].pt1();
719  if (capFirst) {
720  stroker->joinPoints(prev.x, prev.y,
721  tangent,
722  stroker->capStyleMode());
723  } else {
724  stroker->emitMoveTo(qt_real_to_fixed(pt.x()),
725  qt_real_to_fixed(pt.y()));
726  }
727  *startTangent = tangent;
728  first = false;
729  } else {
730  stroker->joinPoints(prev.x, prev.y,
731  tangent,
732  stroker->joinStyleMode());
733  }
734 
735  // Add these beziers
736  for (int i=0; i<count; ++i) {
737  QPointF cp1 = offsetCurves[i].pt2();
738  QPointF cp2 = offsetCurves[i].pt3();
739  QPointF ep = offsetCurves[i].pt4();
740  stroker->emitCubicTo(qt_real_to_fixed(cp1.x()), qt_real_to_fixed(cp1.y()),
741  qt_real_to_fixed(cp2.x()), qt_real_to_fixed(cp2.y()),
742  qt_real_to_fixed(ep.x()), qt_real_to_fixed(ep.y()));
743  }
744  }
745 
746  prev = ep;
747  }
748  }
749 
750  if (start == prev && !stroker->forceOpen()) {
751  // closed subpath, join first and last point
752 #ifdef QPP_STROKE_DEBUG
753  qDebug("\n ---> (side) closed subpath");
754 #endif
755  // don't join empty subpaths
756  if (!first)
757  stroker->joinPoints(prev.x, prev.y, *startTangent, stroker->joinStyleMode());
758  return true;
759  } else {
760 #ifdef QPP_STROKE_DEBUG
761  qDebug("\n ---> (side) open subpath");
762 #endif
763  return false;
764  }
765 }
766 
793 {
794  if (qFuzzyIsNull(angle))
795  return 0;
796 
797  if (qFuzzyCompare(angle, qreal(90)))
798  return 1;
799 
800  qreal radians = qDegreesToRadians(angle);
801  qreal cosAngle = qCos(radians);
802  qreal sinAngle = qSin(radians);
803 
804  // initial guess
805  qreal tc = angle / 90;
806  // do some iterations of newton's method to approximate cosAngle
807  // finds the zero of the function b.pointAt(tc).x() - cosAngle
808  tc -= ((((2-3*QT_PATH_KAPPA) * tc + 3*(QT_PATH_KAPPA-1)) * tc) * tc + 1 - cosAngle) // value
809  / (((6-9*QT_PATH_KAPPA) * tc + 6*(QT_PATH_KAPPA-1)) * tc); // derivative
810  tc -= ((((2-3*QT_PATH_KAPPA) * tc + 3*(QT_PATH_KAPPA-1)) * tc) * tc + 1 - cosAngle) // value
811  / (((6-9*QT_PATH_KAPPA) * tc + 6*(QT_PATH_KAPPA-1)) * tc); // derivative
812 
813  // initial guess
814  qreal ts = tc;
815  // do some iterations of newton's method to approximate sinAngle
816  // finds the zero of the function b.pointAt(tc).y() - sinAngle
817  ts -= ((((3*QT_PATH_KAPPA-2) * ts - 6*QT_PATH_KAPPA + 3) * ts + 3*QT_PATH_KAPPA) * ts - sinAngle)
818  / (((9*QT_PATH_KAPPA-6) * ts + 12*QT_PATH_KAPPA - 6) * ts + 3*QT_PATH_KAPPA);
819  ts -= ((((3*QT_PATH_KAPPA-2) * ts - 6*QT_PATH_KAPPA + 3) * ts + 3*QT_PATH_KAPPA) * ts - sinAngle)
820  / (((9*QT_PATH_KAPPA-6) * ts + 12*QT_PATH_KAPPA - 6) * ts + 3*QT_PATH_KAPPA);
821 
822  // use the average of the t that best approximates cosAngle
823  // and the t that best approximates sinAngle
824  qreal t = 0.5 * (tc + ts);
825 
826 #if 0
827  printf("angle: %f, t: %f\n", angle, t);
828  qreal a, b, c, d;
829  bezierCoefficients(t, a, b, c, d);
830  printf("cosAngle: %.10f, value: %.10f\n", cosAngle, a + b + c * QT_PATH_KAPPA);
831  printf("sinAngle: %.10f, value: %.10f\n", sinAngle, b * QT_PATH_KAPPA + c + d);
832 #endif
833 
834  return t;
835 }
836 
837 Q_GUI_EXPORT void qt_find_ellipse_coords(const QRectF &r, qreal angle, qreal length,
838  QPointF* startPoint, QPointF *endPoint);
839 
852 QPointF qt_curves_for_arc(const QRectF &rect, qreal startAngle, qreal sweepLength,
853  QPointF *curves, int *point_count)
854 {
855  Q_ASSERT(point_count);
856  Q_ASSERT(curves);
857 
858  *point_count = 0;
859  if (qt_is_nan(rect.x()) || qt_is_nan(rect.y()) || qt_is_nan(rect.width()) || qt_is_nan(rect.height())
860  || qt_is_nan(startAngle) || qt_is_nan(sweepLength)) {
861  qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN, results are undefined");
862  return QPointF();
863  }
864 
865  if (rect.isNull()) {
866  return QPointF();
867  }
868 
869  qreal x = rect.x();
870  qreal y = rect.y();
871 
872  qreal w = rect.width();
873  qreal w2 = rect.width() / 2;
874  qreal w2k = w2 * QT_PATH_KAPPA;
875 
876  qreal h = rect.height();
877  qreal h2 = rect.height() / 2;
878  qreal h2k = h2 * QT_PATH_KAPPA;
879 
880  QPointF points[16] =
881  {
882  // start point
883  QPointF(x + w, y + h2),
884 
885  // 0 -> 270 degrees
886  QPointF(x + w, y + h2 + h2k),
887  QPointF(x + w2 + w2k, y + h),
888  QPointF(x + w2, y + h),
889 
890  // 270 -> 180 degrees
891  QPointF(x + w2 - w2k, y + h),
892  QPointF(x, y + h2 + h2k),
893  QPointF(x, y + h2),
894 
895  // 180 -> 90 degrees
896  QPointF(x, y + h2 - h2k),
897  QPointF(x + w2 - w2k, y),
898  QPointF(x + w2, y),
899 
900  // 90 -> 0 degrees
901  QPointF(x + w2 + w2k, y),
902  QPointF(x + w, y + h2 - h2k),
903  QPointF(x + w, y + h2)
904  };
905 
906  if (sweepLength > 360) sweepLength = 360;
907  else if (sweepLength < -360) sweepLength = -360;
908 
909  // Special case fast paths
910  if (startAngle == 0.0) {
911  if (sweepLength == 360.0) {
912  for (int i = 11; i >= 0; --i)
913  curves[(*point_count)++] = points[i];
914  return points[12];
915  } else if (sweepLength == -360.0) {
916  for (int i = 1; i <= 12; ++i)
917  curves[(*point_count)++] = points[i];
918  return points[0];
919  }
920  }
921 
922  int startSegment = int(qFloor(startAngle / 90));
923  int endSegment = int(qFloor((startAngle + sweepLength) / 90));
924 
925  qreal startT = (startAngle - startSegment * 90) / 90;
926  qreal endT = (startAngle + sweepLength - endSegment * 90) / 90;
927 
928  int delta = sweepLength > 0 ? 1 : -1;
929  if (delta < 0) {
930  startT = 1 - startT;
931  endT = 1 - endT;
932  }
933 
934  // avoid empty start segment
935  if (qFuzzyIsNull(startT - qreal(1))) {
936  startT = 0;
937  startSegment += delta;
938  }
939 
940  // avoid empty end segment
941  if (qFuzzyIsNull(endT)) {
942  endT = 1;
943  endSegment -= delta;
944  }
945 
946  startT = qt_t_for_arc_angle(startT * 90);
947  endT = qt_t_for_arc_angle(endT * 90);
948 
949  const bool splitAtStart = !qFuzzyIsNull(startT);
950  const bool splitAtEnd = !qFuzzyIsNull(endT - qreal(1));
951 
952  const int end = endSegment + delta;
953 
954  // empty arc?
955  if (startSegment == end) {
956  const int quadrant = 3 - ((startSegment % 4) + 4) % 4;
957  const int j = 3 * quadrant;
958  return delta > 0 ? points[j + 3] : points[j];
959  }
960 
961  QPointF startPoint, endPoint;
962  qt_find_ellipse_coords(rect, startAngle, sweepLength, &startPoint, &endPoint);
963 
964  for (int i = startSegment; i != end; i += delta) {
965  const int quadrant = 3 - ((i % 4) + 4) % 4;
966  const int j = 3 * quadrant;
967 
968  QBezier b;
969  if (delta > 0)
970  b = QBezier::fromPoints(points[j + 3], points[j + 2], points[j + 1], points[j]);
971  else
972  b = QBezier::fromPoints(points[j], points[j + 1], points[j + 2], points[j + 3]);
973 
974  // empty arc?
975  if (startSegment == endSegment && qFuzzyCompare(startT, endT))
976  return startPoint;
977 
978  if (i == startSegment) {
979  if (i == endSegment && splitAtEnd)
980  b = b.bezierOnInterval(startT, endT);
981  else if (splitAtStart)
982  b = b.bezierOnInterval(startT, 1);
983  } else if (i == endSegment && splitAtEnd) {
984  b = b.bezierOnInterval(0, endT);
985  }
986 
987  // push control points
988  curves[(*point_count)++] = b.pt2();
989  curves[(*point_count)++] = b.pt3();
990  curves[(*point_count)++] = b.pt4();
991  }
992 
993  Q_ASSERT(*point_count > 0);
994  curves[*(point_count)-1] = endPoint;
995 
996  return startPoint;
997 }
998 
999 
1000 static inline void qdashstroker_moveTo(qfixed x, qfixed y, void *data) {
1001  ((QStroker *) data)->moveTo(x, y);
1002 }
1003 
1004 static inline void qdashstroker_lineTo(qfixed x, qfixed y, void *data) {
1005  ((QStroker *) data)->lineTo(x, y);
1006 }
1007 
1008 static inline void qdashstroker_cubicTo(qfixed, qfixed, qfixed, qfixed, qfixed, qfixed, void *) {
1009  Q_ASSERT(0);
1010 // ((QStroker *) data)->cubicTo(c1x, c1y, c2x, c2y, ex, ey);
1011 }
1012 
1013 
1014 /*******************************************************************************
1015  * QDashStroker members
1016  */
1018  : m_stroker(stroker), m_dashOffset(0), m_stroke_width(1), m_miter_limit(1)
1019 {
1020  if (m_stroker) {
1021  setMoveToHook(qdashstroker_moveTo);
1022  setLineToHook(qdashstroker_lineTo);
1023  setCubicToHook(qdashstroker_cubicTo);
1024  }
1025 }
1026 
1028 {
1029 }
1030 
1032 {
1033  const qfixed space = 2;
1034  const qfixed dot = 1;
1035  const qfixed dash = 4;
1036 
1038 
1039  switch (style) {
1040  case Qt::DashLine:
1041  pattern << dash << space;
1042  break;
1043  case Qt::DotLine:
1044  pattern << dot << space;
1045  break;
1046  case Qt::DashDotLine:
1047  pattern << dash << space << dot << space;
1048  break;
1049  case Qt::DashDotDotLine:
1050  pattern << dash << space << dot << space << dot << space;
1051  break;
1052  default:
1053  break;
1054  }
1055 
1056  return pattern;
1057 }
1058 
1059 static inline bool lineRectIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br)
1060 {
1061  return ((p1.x > tl.x || p2.x > tl.x) && (p1.x < br.x || p2.x < br.x)
1062  && (p1.y > tl.y || p2.y > tl.y) && (p1.y < br.y || p2.y < br.y));
1063 }
1064 
1065 // If the line intersects the rectangle, this function will return true.
1066 static bool lineIntersectsRect(qfixed2d p1, qfixed2d p2, const qfixed2d &tl, const qfixed2d &br)
1067 {
1068  if (!lineRectIntersectsRect(p1, p2, tl, br))
1069  return false;
1070  if (p1.x == p2.x || p1.y == p2.y)
1071  return true;
1072 
1073  if (p1.y > p2.y)
1074  qSwap(p1, p2); // make p1 above p2
1075  qfixed2d u;
1076  qfixed2d v;
1077  qfixed2d w = {p2.x - p1.x, p2.y - p1.y};
1078  if (p1.x < p2.x) {
1079  // backslash
1080  u.x = tl.x - p1.x; u.y = br.y - p1.y;
1081  v.x = br.x - p1.x; v.y = tl.y - p1.y;
1082  } else {
1083  // slash
1084  u.x = tl.x - p1.x; u.y = tl.y - p1.y;
1085  v.x = br.x - p1.x; v.y = br.y - p1.y;
1086  }
1087 #if defined(QFIXED_IS_26_6) || defined(QFIXED_IS_16_16)
1088  qint64 val1 = qint64(u.x) * qint64(w.y) - qint64(u.y) * qint64(w.x);
1089  qint64 val2 = qint64(v.x) * qint64(w.y) - qint64(v.y) * qint64(w.x);
1090  return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0);
1091 #elif defined(QFIXED_IS_32_32)
1092  // Cannot do proper test because it may overflow.
1093  return true;
1094 #else
1095  qreal val1 = u.x * w.y - u.y * w.x;
1096  qreal val2 = v.x * w.y - v.y * w.x;
1097  return (val1 < 0 && val2 > 0) || (val1 > 0 && val2 < 0);
1098 #endif
1099 }
1100 
1102 {
1103  int dashCount = qMin(m_dashPattern.size(), 32);
1104  qfixed dashes[32];
1105 
1106  if (m_stroker) {
1110  }
1111 
1112  qreal longestLength = 0;
1113  qreal sumLength = 0;
1114  for (int i=0; i<dashCount; ++i) {
1115  dashes[i] = qMax(m_dashPattern.at(i), qreal(0)) * m_stroke_width;
1116  sumLength += dashes[i];
1117  if (dashes[i] > longestLength)
1118  longestLength = dashes[i];
1119  }
1120 
1121  if (qFuzzyIsNull(sumLength))
1122  return;
1123 
1124  qreal invSumLength = qreal(1) / sumLength;
1125 
1126  Q_ASSERT(dashCount > 0);
1127 
1128  dashCount = dashCount & -2; // Round down to even number
1129 
1130  int idash = 0; // Index to current dash
1131  qreal pos = 0; // The position on the curve, 0 <= pos <= path.length
1132  qreal elen = 0; // element length
1133  qreal doffset = m_dashOffset * m_stroke_width;
1134 
1135  // make sure doffset is in range [0..sumLength)
1136  doffset = std::fmod(doffset, sumLength);
1137  if (doffset < 0)
1138  doffset += sumLength;
1139 
1140  while (doffset >= dashes[idash]) {
1141  doffset -= dashes[idash];
1142  if (++idash >= dashCount)
1143  idash = 0;
1144  }
1145 
1146  qreal estart = 0; // The elements starting position
1147  qreal estop = 0; // The element stop position
1148 
1149  QLineF cline;
1150 
1152  qfixed2d prev = it.next();
1153  if (!prev.isFinite())
1154  return;
1155 
1156  bool clipping = !m_clip_rect.isEmpty();
1157  qfixed2d move_to_pos = prev;
1158  qfixed2d line_to_pos;
1159 
1160  // Pad to avoid clipping the borders of thick pens.
1161  qfixed padding = qt_real_to_fixed(qMax(m_stroke_width, m_miter_limit) * longestLength);
1162  qfixed2d clip_tl = { qt_real_to_fixed(m_clip_rect.left()) - padding,
1163  qt_real_to_fixed(m_clip_rect.top()) - padding };
1164  qfixed2d clip_br = { qt_real_to_fixed(m_clip_rect.right()) + padding ,
1165  qt_real_to_fixed(m_clip_rect.bottom()) + padding };
1166 
1167  bool hasMoveTo = false;
1168  while (it.hasNext()) {
1169  QStrokerOps::Element e = it.next();
1170  if (!qfixed2d(e).isFinite())
1171  continue;
1172 
1173  Q_ASSERT(e.isLineTo());
1174  cline = QLineF(qt_fixed_to_real(prev.x),
1175  qt_fixed_to_real(prev.y),
1176  qt_fixed_to_real(e.x),
1177  qt_fixed_to_real(e.y));
1178  elen = cline.length();
1179 
1180  estop = estart + elen;
1181 
1182  bool done = pos >= estop;
1183 
1184  // Check if the entire line should be clipped away or simplified
1185  bool clipIt = clipping && !lineIntersectsRect(prev, e, clip_tl, clip_br);
1186  bool skipDashing = elen * invSumLength > repetitionLimit();
1187  int maxDashes = dashCount;
1188  if (skipDashing || clipIt) {
1189  // Cut away full dash sequences.
1190  elen -= std::floor(elen * invSumLength) * sumLength;
1191  // Update dash offset.
1192  while (!done) {
1193  qreal dpos = pos + dashes[idash] - doffset - estart;
1194 
1195  Q_ASSERT(dpos >= 0);
1196 
1197  if (dpos > elen) { // dash extends this line
1198  doffset = dashes[idash] - (dpos - elen); // subtract the part already used
1199  pos = estop; // move pos to next path element
1200  done = true;
1201  } else { // Dash is on this line
1202  pos = --maxDashes > 0 ? dpos + estart : estop;
1203  done = pos >= estop;
1204  if (++idash >= dashCount)
1205  idash = 0;
1206  doffset = 0; // full segment so no offset on next.
1207  }
1208  }
1209  if (clipIt) {
1210  hasMoveTo = false;
1211  } else {
1212  // skip costly dashing, just draw solid line
1213  if (!hasMoveTo) {
1214  emitMoveTo(move_to_pos.x, move_to_pos.y);
1215  hasMoveTo = true;
1216  }
1217  emitLineTo(e.x, e.y);
1218  }
1219  move_to_pos = e;
1220  }
1221 
1222  // Dash away...
1223  while (!done) {
1224  QPointF p2;
1225 
1226  bool has_offset = doffset > 0;
1227  bool evenDash = (idash & 1) == 0;
1228  qreal dpos = pos + dashes[idash] - doffset - estart;
1229 
1230  Q_ASSERT(dpos >= 0);
1231 
1232  if (dpos > elen) { // dash extends this line
1233  doffset = dashes[idash] - (dpos - elen); // subtract the part already used
1234  pos = estop; // move pos to next path element
1235  done = true;
1236  p2 = cline.p2();
1237  } else { // Dash is on this line
1238  p2 = cline.pointAt(dpos/elen);
1239  pos = dpos + estart;
1240  done = pos >= estop;
1241  if (++idash >= dashCount)
1242  idash = 0;
1243  doffset = 0; // full segment so no offset on next.
1244  }
1245 
1246  if (evenDash) {
1247  line_to_pos.x = qt_real_to_fixed(p2.x());
1248  line_to_pos.y = qt_real_to_fixed(p2.y());
1249 
1250  if (!clipping
1251  || lineRectIntersectsRect(move_to_pos, line_to_pos, clip_tl, clip_br))
1252  {
1253  // If we have an offset, we're continuing a dash
1254  // from a previous element and should only
1255  // continue the current dash, without starting a
1256  // new subpath.
1257  if (!has_offset || !hasMoveTo) {
1258  emitMoveTo(move_to_pos.x, move_to_pos.y);
1259  hasMoveTo = true;
1260  }
1261 
1262  emitLineTo(line_to_pos.x, line_to_pos.y);
1263  } else {
1264  hasMoveTo = false;
1265  }
1266  move_to_pos = line_to_pos;
1267  } else {
1268  move_to_pos.x = qt_real_to_fixed(p2.x());
1269  move_to_pos.y = qt_real_to_fixed(p2.y());
1270  }
1271  }
1272 
1273  // Shuffle to the next cycle...
1274  estart = estop;
1275  prev = e;
1276  }
1277 
1278 }
1279 
small capitals from c petite p scientific f u
Definition: afcover.h:88
small capitals from c petite p scientific i
[1]
Definition: afcover.h:80
Arabic default style
Definition: afstyles.h:94
QPointF pt1() const
Definition: qbezier_p.h:95
QPointF pt3() const
Definition: qbezier_p.h:97
static QBezier fromPoints(const QPointF &p1, const QPointF &p2, const QPointF &p3, const QPointF &p4)
Definition: qbezier_p.h:70
QPolygonF toPolygon(qreal bezier_flattening_threshold=0.5) const
Definition: qbezier.cpp:57
QLineF startTangent() const
Definition: qbezier_p.h:133
int shifted(QBezier *curveSegments, int maxSegmets, qreal offset, float threshold) const
Definition: qbezier.cpp:391
QPointF pt4() const
Definition: qbezier_p.h:98
QPointF pt2() const
Definition: qbezier_p.h:96
qreal m_miter_limit
Definition: qstroker_p.h:293
void processCurrentSubpath() override
Definition: qstroker.cpp:1101
QStroker * m_stroker
Definition: qstroker_p.h:288
QDashStroker(QStroker *stroker)
Definition: qstroker.cpp:1017
QList< qfixed > m_dashPattern
Definition: qstroker_p.h:289
static int repetitionLimit()
Definition: qstroker_p.h:271
static QList< qfixed > patternForStyle(Qt::PenStyle style)
Definition: qstroker.cpp:1031
qreal m_dashOffset
Definition: qstroker_p.h:290
qreal m_stroke_width
Definition: qstroker_p.h:292
Type & at(int i)
Definition: qdatabuffer_p.h:93
int size() const
Definition: qdatabuffer_p.h:90
The QLineF class provides a two-dimensional vector using floating point precision.
Definition: qline.h:215
constexpr QPointF p1() const
Definition: qline.h:322
constexpr qreal x1() const
Definition: qline.h:297
constexpr bool isNull() const
Definition: qline.h:317
constexpr qreal y2() const
Definition: qline.h:312
void translate(const QPointF &p)
Definition: qline.h:347
constexpr qreal dx() const
Definition: qline.h:332
constexpr qreal dy() const
Definition: qline.h:337
qreal angleTo(const QLineF &l) const
Definition: qline.cpp:794
qreal length() const
Definition: qline.cpp:569
IntersectionType
Definition: qline.h:218
@ NoIntersection
Definition: qline.h:218
@ BoundedIntersection
Definition: qline.h:218
IntersectionType intersects(const QLineF &l, QPointF *intersectionPoint=nullptr) const
Definition: qline.cpp:674
constexpr qreal x2() const
Definition: qline.h:307
constexpr QPointF p2() const
Definition: qline.h:327
constexpr QPointF pointAt(qreal t) const
Definition: qline.h:384
void setLength(qreal len)
Definition: qline.h:373
constexpr qreal y1() const
Definition: qline.h:302
qsizetype size() const noexcept
Definition: qlist.h:414
const_reference at(qsizetype i) const noexcept
Definition: qlist.h:457
bool qFuzzyCompare(const QMatrix4x4 &m1, const QMatrix4x4 &m2)
Definition: qmatrix4x4.cpp:774
The QPainterPath::Element class specifies the position and type of a subpath.
Definition: qpainterpath.h:74
The QPainterPath class provides a container for painting operations, enabling graphical shapes to be ...
Definition: qpainterpath.h:65
The QPointF class defines a point in the plane using floating point precision.
Definition: qpoint.h:242
constexpr qreal x() const noexcept
Definition: qpoint.h:361
constexpr static qreal dotProduct(const QPointF &p1, const QPointF &p2)
Definition: qpoint.h:267
constexpr qreal y() const noexcept
Definition: qpoint.h:366
The QPolygonF class provides a list of points using floating point precision. \inmodule QtGui.
Definition: qpolygon.h:128
The QRectF class defines a finite rectangle in the plane using floating point precision.
Definition: qrect.h:511
constexpr bool isEmpty() const noexcept
Definition: qrect.h:670
constexpr qreal bottom() const noexcept
Definition: qrect.h:527
constexpr qreal left() const noexcept
Definition: qrect.h:524
constexpr qreal top() const noexcept
Definition: qrect.h:525
constexpr qreal right() const noexcept
Definition: qrect.h:526
qfixed m_back1Y
Definition: qstroker_p.h:254
qfixed strokeWidth() const
Definition: qstroker_p.h:217
void emitLineTo(qfixed x, qfixed y)
Definition: qstroker_p.h:356
static Qt::PenJoinStyle joinForJoinMode(LineJoinMode mode)
Definition: qstroker.cpp:394
qfixed m_back2Y
Definition: qstroker_p.h:257
void processCurrentSubpath() override
Definition: qstroker.cpp:415
static LineJoinMode joinModeForJoin(Qt::PenJoinStyle joinStyle)
Definition: qstroker.cpp:402
LineJoinMode capStyleMode() const
Definition: qstroker_p.h:221
qfixed miterLimit() const
Definition: qstroker_p.h:228
LineJoinMode m_capStyle
Definition: qstroker_p.h:250
void joinPoints(qfixed x, qfixed y, const QLineF &nextLine, LineJoinMode join)
Definition: qstroker.cpp:437
Qt::PenJoinStyle joinStyle() const
Definition: qstroker_p.h:224
void emitMoveTo(qfixed x, qfixed y)
Definition: qstroker_p.h:347
qfixed m_back1X
Definition: qstroker_p.h:253
qfixed m_strokeWidth
Definition: qstroker_p.h:247
qfixed m_miterLimit
Definition: qstroker_p.h:248
LineJoinMode joinStyleMode() const
Definition: qstroker_p.h:225
static LineJoinMode joinModeForCap(Qt::PenCapStyle)
Definition: qstroker.cpp:387
bool forceOpen() const
Definition: qstroker_p.h:231
@ SquareJoin
Definition: qstroker_p.h:202
@ SvgMiterJoin
Definition: qstroker_p.h:206
qfixed m_back2X
Definition: qstroker_p.h:256
static Qt::PenCapStyle capForJoinMode(LineJoinMode mode)
Definition: qstroker.cpp:380
void emitCubicTo(qfixed c1x, qfixed c1y, qfixed c2x, qfixed c2y, qfixed ex, qfixed ey)
Definition: qstroker_p.h:365
virtual void begin(void *customData)
Definition: qstroker.cpp:207
void setCubicToHook(qStrokerCubicToHook cubicToHook)
Definition: qstroker_p.h:150
void moveTo(qfixed x, qfixed y)
Definition: qstroker_p.h:319
void setMoveToHook(qStrokerMoveToHook moveToHook)
Definition: qstroker_p.h:148
void setCurveThresholdFromTransform(const QTransform &transform)
Definition: qstroker_p.h:167
void strokeEllipse(const QRectF &ellipse, void *data, const QTransform &matrix)
Definition: qstroker.cpp:340
void strokePath(const QPainterPath &path, void *data, const QTransform &matrix)
Definition: qstroker.cpp:236
void setLineToHook(qStrokerLineToHook lineToHook)
Definition: qstroker_p.h:149
QDataBuffer< Element > m_elements
Definition: qstroker_p.h:183
void cubicTo(qfixed x1, qfixed y1, qfixed x2, qfixed y2, qfixed ex, qfixed ey)
Definition: qstroker_p.h:334
void strokePolygon(const QPointF *points, int pointCount, bool implicit_close, void *data, const QTransform &matrix)
Definition: qstroker.cpp:307
void emitLineTo(qfixed x, qfixed y)
Definition: qstroker_p.h:307
virtual void end()
Definition: qstroker.cpp:218
virtual ~QStrokerOps()
Definition: qstroker.cpp:195
qfixed m_dashThreshold
Definition: qstroker_p.h:187
void * m_customData
Definition: qstroker_p.h:189
void emitMoveTo(qfixed x, qfixed y)
Definition: qstroker_p.h:301
qfixed curveThreshold() const
Definition: qstroker_p.h:175
void lineTo(qfixed x, qfixed y)
Definition: qstroker_p.h:328
QRectF m_clip_rect
Definition: qstroker_p.h:185
virtual void processCurrentSubpath()=0
QStrokerOps::Element next()
Definition: qstroker.cpp:74
QSubpathBackwardIterator(const QDataBuffer< QStrokerOps::Element > *path)
Definition: qstroker.cpp:67
bool hasNext() const
Definition: qstroker.cpp:72
QSubpathFlatIterator(const QDataBuffer< QStrokerOps::Element > *path, qreal threshold)
Definition: qstroker.cpp:120
QStrokerOps::Element next()
Definition: qstroker.cpp:125
bool hasNext() const
Definition: qstroker.cpp:123
QStrokerOps::Element next()
Definition: qstroker.cpp:57
bool hasNext() const
Definition: qstroker.cpp:56
QSubpathForwardIterator(const QDataBuffer< QStrokerOps::Element > *path)
Definition: qstroker.cpp:53
int position() const
Definition: qstroker.cpp:55
The QTransform class specifies 2D transformations of a coordinate system.
Definition: qtransform.h:56
QPixmap p2
QPixmap p1
[0]
qSwap(pi, e)
double e
rect
[4]
PenStyle
Definition: qnamespace.h:1111
@ DashDotDotLine
Definition: qnamespace.h:1117
@ DotLine
Definition: qnamespace.h:1115
@ DashDotLine
Definition: qnamespace.h:1116
@ DashLine
Definition: qnamespace.h:1114
PenJoinStyle
Definition: qnamespace.h:1131
@ SvgMiterJoin
Definition: qnamespace.h:1135
@ BevelJoin
Definition: qnamespace.h:1133
@ MiterJoin
Definition: qnamespace.h:1132
@ RoundJoin
Definition: qnamespace.h:1134
PenCapStyle
Definition: qnamespace.h:1124
@ SquareCap
Definition: qnamespace.h:1126
@ RoundCap
Definition: qnamespace.h:1127
@ FlatCap
Definition: qnamespace.h:1125
QT_BEGIN_NAMESPACE bool done
bool qFuzzyCompare(qfloat16 p1, qfloat16 p2) noexcept
Definition: qfloat16.h:233
bool qFuzzyIsNull(qfloat16 f) noexcept
Definition: qfloat16.h:249
QT_END_INCLUDE_NAMESPACE typedef double qreal
Definition: qglobal.h:341
long long qint64
Definition: qglobal.h:298
#define qDebug
[1]
Definition: qlogging.h:177
#define qWarning
Definition: qlogging.h:179
int qFloor(T v)
Definition: qmath.h:78
auto qCos(T v)
Definition: qmath.h:96
auto qSin(T v)
Definition: qmath.h:90
constexpr float qDegreesToRadians(float degrees)
Definition: qmath.h:296
GLenum GLuint GLenum GLsizei length
Definition: qopengl.h:270
GLenum type
Definition: qopengl.h:270
GLboolean GLboolean GLboolean b
GLsizei const GLfloat * v
[13]
GLint GLint GLint GLint GLint x
[0]
GLenum mode
GLboolean r
[2]
GLfloat GLfloat GLfloat w
[0]
GLboolean GLboolean GLboolean GLboolean a
[7]
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLenum GLenum GLsizei count
GLfloat angle
GLint GLsizei GLsizei GLenum GLenum GLsizei void * data
GLuint start
GLenum GLuint GLintptr offset
GLint first
GLsizei dashCount
GLint y
GLsizei startSegment
GLfloat GLfloat GLfloat GLfloat h
GLfixed GLfixed GLint GLint GLfixed points
Definition: qopenglext.h:5206
GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint GLdouble GLdouble w2
Definition: qopenglext.h:12395
const GLubyte * c
Definition: qopenglext.h:12701
const GLfloat * tc
Definition: qopenglext.h:12721
GLuint GLenum matrix
Definition: qopenglext.h:11564
GLdouble GLdouble t
[9]
Definition: qopenglext.h:243
GLsizei const GLchar *const * path
Definition: qopenglext.h:4283
GLubyte * pattern
Definition: qopenglext.h:2744
#define Q_ASSERT(cond)
Definition: qrandom.cpp:84
QPointF qAbs(const QPointF &p)
Definition: qscroller.cpp:119
Q_GUI_EXPORT void qt_find_ellipse_coords(const QRectF &r, qreal angle, qreal length, QPointF *startPoint, QPointF *endPoint)
qreal qt_t_for_arc_angle(qreal angle)
Definition: qstroker.cpp:792
bool qt_stroke_side(Iterator *it, QStroker *stroker, bool capFirst, QLineF *startTangent)
Definition: qstroker.cpp:632
QPointF qt_curves_for_arc(const QRectF &rect, qreal startAngle, qreal sweepLength, QPointF *curves, int *point_count)
Definition: qstroker.cpp:852
#define QT_PATH_KAPPA
Definition: qstroker_p.h:113
#define qt_fixed_to_real(fixed)
Definition: qstroker_p.h:101
#define qt_real_to_fixed(real)
Definition: qstroker_p.h:100
QT_BEGIN_NAMESPACE typedef qreal qfixed
Definition: qstroker_p.h:99
Q_UNUSED(salary)
[21]
QObject::connect nullptr
QStringList::Iterator it
bool isMoveTo() const
Definition: qstroker_p.h:138
QPainterPath::ElementType type
Definition: qstroker_p.h:134
qfixed x
Definition: qstroker_p.h:104
bool isFinite()
Definition: qstroker_p.h:107
qfixed y
Definition: qstroker_p.h:105