|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the QtGui module of the Qt Toolkit. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "qpainterpath.h" |
|
43 #include "qpainterpath_p.h" |
|
44 |
|
45 #include <qbitmap.h> |
|
46 #include <qdebug.h> |
|
47 #include <qiodevice.h> |
|
48 #include <qlist.h> |
|
49 #include <qmatrix.h> |
|
50 #include <qpen.h> |
|
51 #include <qpolygon.h> |
|
52 #include <qtextlayout.h> |
|
53 #include <qvarlengtharray.h> |
|
54 #include <qmath.h> |
|
55 |
|
56 #include <private/qbezier_p.h> |
|
57 #include <private/qfontengine_p.h> |
|
58 #include <private/qnumeric_p.h> |
|
59 #include <private/qobject_p.h> |
|
60 #include <private/qpathclipper_p.h> |
|
61 #include <private/qstroker_p.h> |
|
62 #include <private/qtextengine_p.h> |
|
63 |
|
64 #include <limits.h> |
|
65 |
|
66 #if 0 |
|
67 #include <performance.h> |
|
68 #else |
|
69 #define PM_INIT |
|
70 #define PM_MEASURE(x) |
|
71 #define PM_DISPLAY |
|
72 #endif |
|
73 |
|
74 QT_BEGIN_NAMESPACE |
|
75 |
|
76 struct QPainterPathPrivateDeleter |
|
77 { |
|
78 static inline void cleanup(QPainterPathPrivate *d) |
|
79 { |
|
80 // note - we must up-cast to QPainterPathData since QPainterPathPrivate |
|
81 // has a non-virtual destructor! |
|
82 if (d && !d->ref.deref()) |
|
83 delete static_cast<QPainterPathData *>(d); |
|
84 } |
|
85 }; |
|
86 |
|
87 // This value is used to determine the length of control point vectors |
|
88 // when approximating arc segments as curves. The factor is multiplied |
|
89 // with the radius of the circle. |
|
90 |
|
91 // #define QPP_DEBUG |
|
92 // #define QPP_STROKE_DEBUG |
|
93 //#define QPP_FILLPOLYGONS_DEBUG |
|
94 |
|
95 QPainterPath qt_stroke_dash(const QPainterPath &path, qreal *dashes, int dashCount); |
|
96 |
|
97 void qt_find_ellipse_coords(const QRectF &r, qreal angle, qreal length, |
|
98 QPointF* startPoint, QPointF *endPoint) |
|
99 { |
|
100 if (r.isNull()) { |
|
101 if (startPoint) |
|
102 *startPoint = QPointF(); |
|
103 if (endPoint) |
|
104 *endPoint = QPointF(); |
|
105 return; |
|
106 } |
|
107 |
|
108 qreal w2 = r.width() / 2; |
|
109 qreal h2 = r.height() / 2; |
|
110 |
|
111 qreal angles[2] = { angle, angle + length }; |
|
112 QPointF *points[2] = { startPoint, endPoint }; |
|
113 |
|
114 for (int i = 0; i < 2; ++i) { |
|
115 if (!points[i]) |
|
116 continue; |
|
117 |
|
118 qreal theta = angles[i] - 360 * qFloor(angles[i] / 360); |
|
119 qreal t = theta / 90; |
|
120 // truncate |
|
121 int quadrant = int(t); |
|
122 t -= quadrant; |
|
123 |
|
124 t = qt_t_for_arc_angle(90 * t); |
|
125 |
|
126 // swap x and y? |
|
127 if (quadrant & 1) |
|
128 t = 1 - t; |
|
129 |
|
130 qreal a, b, c, d; |
|
131 QBezier::coefficients(t, a, b, c, d); |
|
132 QPointF p(a + b + c*QT_PATH_KAPPA, d + c + b*QT_PATH_KAPPA); |
|
133 |
|
134 // left quadrants |
|
135 if (quadrant == 1 || quadrant == 2) |
|
136 p.rx() = -p.x(); |
|
137 |
|
138 // top quadrants |
|
139 if (quadrant == 0 || quadrant == 1) |
|
140 p.ry() = -p.y(); |
|
141 |
|
142 *points[i] = r.center() + QPointF(w2 * p.x(), h2 * p.y()); |
|
143 } |
|
144 } |
|
145 |
|
146 #ifdef QPP_DEBUG |
|
147 static void qt_debug_path(const QPainterPath &path) |
|
148 { |
|
149 const char *names[] = { |
|
150 "MoveTo ", |
|
151 "LineTo ", |
|
152 "CurveTo ", |
|
153 "CurveToData" |
|
154 }; |
|
155 |
|
156 printf("\nQPainterPath: elementCount=%d\n", path.elementCount()); |
|
157 for (int i=0; i<path.elementCount(); ++i) { |
|
158 const QPainterPath::Element &e = path.elementAt(i); |
|
159 Q_ASSERT(e.type >= 0 && e.type <= QPainterPath::CurveToDataElement); |
|
160 printf(" - %3d:: %s, (%.2f, %.2f)\n", i, names[e.type], e.x, e.y); |
|
161 } |
|
162 } |
|
163 #endif |
|
164 |
|
165 /*! |
|
166 \class QPainterPath |
|
167 \ingroup painting |
|
168 \ingroup shared |
|
169 |
|
170 \brief The QPainterPath class provides a container for painting operations, |
|
171 enabling graphical shapes to be constructed and reused. |
|
172 |
|
173 A painter path is an object composed of a number of graphical |
|
174 building blocks, such as rectangles, ellipses, lines, and curves. |
|
175 Building blocks can be joined in closed subpaths, for example as a |
|
176 rectangle or an ellipse. A closed path has coinciding start and |
|
177 end points. Or they can exist independently as unclosed subpaths, |
|
178 such as lines and curves. |
|
179 |
|
180 A QPainterPath object can be used for filling, outlining, and |
|
181 clipping. To generate fillable outlines for a given painter path, |
|
182 use the QPainterPathStroker class. The main advantage of painter |
|
183 paths over normal drawing operations is that complex shapes only |
|
184 need to be created once; then they can be drawn many times using |
|
185 only calls to the QPainter::drawPath() function. |
|
186 |
|
187 QPainterPath provides a collection of functions that can be used |
|
188 to obtain information about the path and its elements. In addition |
|
189 it is possible to reverse the order of the elements using the |
|
190 toReversed() function. There are also several functions to convert |
|
191 this painter path object into a polygon representation. |
|
192 |
|
193 \tableofcontents |
|
194 |
|
195 \section1 Composing a QPainterPath |
|
196 |
|
197 A QPainterPath object can be constructed as an empty path, with a |
|
198 given start point, or as a copy of another QPainterPath object. |
|
199 Once created, lines and curves can be added to the path using the |
|
200 lineTo(), arcTo(), cubicTo() and quadTo() functions. The lines and |
|
201 curves stretch from the currentPosition() to the position passed |
|
202 as argument. |
|
203 |
|
204 The currentPosition() of the QPainterPath object is always the end |
|
205 position of the last subpath that was added (or the initial start |
|
206 point). Use the moveTo() function to move the currentPosition() |
|
207 without adding a component. The moveTo() function implicitly |
|
208 starts a new subpath, and closes the previous one. Another way of |
|
209 starting a new subpath is to call the closeSubpath() function |
|
210 which closes the current path by adding a line from the |
|
211 currentPosition() back to the path's start position. Note that the |
|
212 new path will have (0, 0) as its initial currentPosition(). |
|
213 |
|
214 QPainterPath class also provides several convenience functions to |
|
215 add closed subpaths to a painter path: addEllipse(), addPath(), |
|
216 addRect(), addRegion() and addText(). The addPolygon() function |
|
217 adds an \e unclosed subpath. In fact, these functions are all |
|
218 collections of moveTo(), lineTo() and cubicTo() operations. |
|
219 |
|
220 In addition, a path can be added to the current path using the |
|
221 connectPath() function. But note that this function will connect |
|
222 the last element of the current path to the first element of given |
|
223 one by adding a line. |
|
224 |
|
225 Below is a code snippet that shows how a QPainterPath object can |
|
226 be used: |
|
227 |
|
228 \table 100% |
|
229 \row |
|
230 \o \inlineimage qpainterpath-construction.png |
|
231 \o |
|
232 \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 0 |
|
233 \endtable |
|
234 |
|
235 The painter path is initially empty when constructed. We first add |
|
236 a rectangle, which is a closed subpath. Then we add two bezier |
|
237 curves which together form a closed subpath even though they are |
|
238 not closed individually. Finally we draw the entire path. The path |
|
239 is filled using the default fill rule, Qt::OddEvenFill. Qt |
|
240 provides two methods for filling paths: |
|
241 |
|
242 \table |
|
243 \row |
|
244 \o \inlineimage qt-fillrule-oddeven.png |
|
245 \o \inlineimage qt-fillrule-winding.png |
|
246 \header |
|
247 \o Qt::OddEvenFill |
|
248 \o Qt::WindingFill |
|
249 \endtable |
|
250 |
|
251 See the Qt::FillRule documentation for the definition of the |
|
252 rules. A painter path's currently set fill rule can be retrieved |
|
253 using the fillRule() function, and altered using the setFillRule() |
|
254 function. |
|
255 |
|
256 \section1 QPainterPath Information |
|
257 |
|
258 The QPainterPath class provides a collection of functions that |
|
259 returns information about the path and its elements. |
|
260 |
|
261 The currentPosition() function returns the end point of the last |
|
262 subpath that was added (or the initial start point). The |
|
263 elementAt() function can be used to retrieve the various subpath |
|
264 elements, the \e number of elements can be retrieved using the |
|
265 elementCount() function, and the isEmpty() function tells whether |
|
266 this QPainterPath object contains any elements at all. |
|
267 |
|
268 The controlPointRect() function returns the rectangle containing |
|
269 all the points and control points in this path. This function is |
|
270 significantly faster to compute than the exact boundingRect() |
|
271 which returns the bounding rectangle of this painter path with |
|
272 floating point precision. |
|
273 |
|
274 Finally, QPainterPath provides the contains() function which can |
|
275 be used to determine whether a given point or rectangle is inside |
|
276 the path, and the intersects() function which determines if any of |
|
277 the points inside a given rectangle also are inside this path. |
|
278 |
|
279 \section1 QPainterPath Conversion |
|
280 |
|
281 For compatibility reasons, it might be required to simplify the |
|
282 representation of a painter path: QPainterPath provides the |
|
283 toFillPolygon(), toFillPolygons() and toSubpathPolygons() |
|
284 functions which convert the painter path into a polygon. The |
|
285 toFillPolygon() returns the painter path as one single polygon, |
|
286 while the two latter functions return a list of polygons. |
|
287 |
|
288 The toFillPolygons() and toSubpathPolygons() functions are |
|
289 provided because it is usually faster to draw several small |
|
290 polygons than to draw one large polygon, even though the total |
|
291 number of points drawn is the same. The difference between the two |
|
292 is the \e number of polygons they return: The toSubpathPolygons() |
|
293 creates one polygon for each subpath regardless of intersecting |
|
294 subpaths (i.e. overlapping bounding rectangles), while the |
|
295 toFillPolygons() functions creates only one polygon for |
|
296 overlapping subpaths. |
|
297 |
|
298 The toFillPolygon() and toFillPolygons() functions first convert |
|
299 all the subpaths to polygons, then uses a rewinding technique to |
|
300 make sure that overlapping subpaths can be filled using the |
|
301 correct fill rule. Note that rewinding inserts additional lines in |
|
302 the polygon so the outline of the fill polygon does not match the |
|
303 outline of the path. |
|
304 |
|
305 \section1 Examples |
|
306 |
|
307 Qt provides the \l {painting/painterpaths}{Painter Paths Example} |
|
308 and the \l {demos/deform}{Vector Deformation Demo} which are |
|
309 located in Qt's example and demo directories respectively. |
|
310 |
|
311 The \l {painting/painterpaths}{Painter Paths Example} shows how |
|
312 painter paths can be used to build complex shapes for rendering |
|
313 and lets the user experiment with the filling and stroking. The |
|
314 \l {demos/deform}{Vector Deformation Demo} shows how to use |
|
315 QPainterPath to draw text. |
|
316 |
|
317 \table |
|
318 \row |
|
319 \o \inlineimage qpainterpath-example.png |
|
320 \o \inlineimage qpainterpath-demo.png |
|
321 \header |
|
322 \o \l {painting/painterpaths}{Painter Paths Example} |
|
323 \o \l {demos/deform}{Vector Deformation Demo} |
|
324 \endtable |
|
325 |
|
326 \sa QPainterPathStroker, QPainter, QRegion, {Painter Paths Example} |
|
327 */ |
|
328 |
|
329 /*! |
|
330 \enum QPainterPath::ElementType |
|
331 |
|
332 This enum describes the types of elements used to connect vertices |
|
333 in subpaths. |
|
334 |
|
335 Note that elements added as closed subpaths using the |
|
336 addEllipse(), addPath(), addPolygon(), addRect(), addRegion() and |
|
337 addText() convenience functions, is actually added to the path as |
|
338 a collection of separate elements using the moveTo(), lineTo() and |
|
339 cubicTo() functions. |
|
340 |
|
341 \value MoveToElement A new subpath. See also moveTo(). |
|
342 \value LineToElement A line. See also lineTo(). |
|
343 \value CurveToElement A curve. See also cubicTo() and quadTo(). |
|
344 \value CurveToDataElement The extra data required to describe a curve in |
|
345 a CurveToElement element. |
|
346 |
|
347 \sa elementAt(), elementCount() |
|
348 */ |
|
349 |
|
350 /*! |
|
351 \class QPainterPath::Element |
|
352 |
|
353 \brief The QPainterPath::Element class specifies the position and |
|
354 type of a subpath. |
|
355 |
|
356 Once a QPainterPath object is constructed, subpaths like lines and |
|
357 curves can be added to the path (creating |
|
358 QPainterPath::LineToElement and QPainterPath::CurveToElement |
|
359 components). |
|
360 |
|
361 The lines and curves stretch from the currentPosition() to the |
|
362 position passed as argument. The currentPosition() of the |
|
363 QPainterPath object is always the end position of the last subpath |
|
364 that was added (or the initial start point). The moveTo() function |
|
365 can be used to move the currentPosition() without adding a line or |
|
366 curve, creating a QPainterPath::MoveToElement component. |
|
367 |
|
368 \sa QPainterPath |
|
369 */ |
|
370 |
|
371 /*! |
|
372 \variable QPainterPath::Element::x |
|
373 \brief the x coordinate of the element's position. |
|
374 |
|
375 \sa {operator QPointF()} |
|
376 */ |
|
377 |
|
378 /*! |
|
379 \variable QPainterPath::Element::y |
|
380 \brief the y coordinate of the element's position. |
|
381 |
|
382 \sa {operator QPointF()} |
|
383 */ |
|
384 |
|
385 /*! |
|
386 \variable QPainterPath::Element::type |
|
387 \brief the type of element |
|
388 |
|
389 \sa isCurveTo(), isLineTo(), isMoveTo() |
|
390 */ |
|
391 |
|
392 /*! |
|
393 \fn bool QPainterPath::Element::operator==(const Element &other) const |
|
394 \since 4.2 |
|
395 |
|
396 Returns true if this element is equal to \a other; |
|
397 otherwise returns false. |
|
398 |
|
399 \sa operator!=() |
|
400 */ |
|
401 |
|
402 /*! |
|
403 \fn bool QPainterPath::Element::operator!=(const Element &other) const |
|
404 \since 4.2 |
|
405 |
|
406 Returns true if this element is not equal to \a other; |
|
407 otherwise returns false. |
|
408 |
|
409 \sa operator==() |
|
410 */ |
|
411 |
|
412 /*! |
|
413 \fn bool QPainterPath::Element::isCurveTo () const |
|
414 |
|
415 Returns true if the element is a curve, otherwise returns false. |
|
416 |
|
417 \sa type, QPainterPath::CurveToElement |
|
418 */ |
|
419 |
|
420 /*! |
|
421 \fn bool QPainterPath::Element::isLineTo () const |
|
422 |
|
423 Returns true if the element is a line, otherwise returns false. |
|
424 |
|
425 \sa type, QPainterPath::LineToElement |
|
426 */ |
|
427 |
|
428 /*! |
|
429 \fn bool QPainterPath::Element::isMoveTo () const |
|
430 |
|
431 Returns true if the element is moving the current position, |
|
432 otherwise returns false. |
|
433 |
|
434 \sa type, QPainterPath::MoveToElement |
|
435 */ |
|
436 |
|
437 /*! |
|
438 \fn QPainterPath::Element::operator QPointF () const |
|
439 |
|
440 Returns the element's position. |
|
441 |
|
442 \sa x, y |
|
443 */ |
|
444 |
|
445 /*! |
|
446 \fn void QPainterPath::addEllipse(qreal x, qreal y, qreal width, qreal height) |
|
447 \overload |
|
448 |
|
449 Creates an ellipse within the bounding rectangle defined by its top-left |
|
450 corner at (\a x, \a y), \a width and \a height, and adds it to the |
|
451 painter path as a closed subpath. |
|
452 */ |
|
453 |
|
454 /*! |
|
455 \since 4.4 |
|
456 |
|
457 \fn void QPainterPath::addEllipse(const QPointF ¢er, qreal rx, qreal ry) |
|
458 \overload |
|
459 |
|
460 Creates an ellipse positioned at \a{center} with radii \a{rx} and \a{ry}, |
|
461 and adds it to the painter path as a closed subpath. |
|
462 */ |
|
463 |
|
464 /*! |
|
465 \fn void QPainterPath::addText(qreal x, qreal y, const QFont &font, const QString &text) |
|
466 \overload |
|
467 |
|
468 Adds the given \a text to this path as a set of closed subpaths created |
|
469 from the \a font supplied. The subpaths are positioned so that the left |
|
470 end of the text's baseline lies at the point specified by (\a x, \a y). |
|
471 */ |
|
472 |
|
473 /*! |
|
474 \fn int QPainterPath::elementCount() const |
|
475 |
|
476 Returns the number of path elements in the painter path. |
|
477 |
|
478 \sa ElementType, elementAt(), isEmpty() |
|
479 */ |
|
480 |
|
481 /*! |
|
482 \fn const QPainterPath::Element &QPainterPath::elementAt(int index) const |
|
483 |
|
484 Returns the element at the given \a index in the painter path. |
|
485 |
|
486 \sa ElementType, elementCount(), isEmpty() |
|
487 */ |
|
488 |
|
489 /*! |
|
490 \fn void QPainterPath::setElementPositionAt(int index, qreal x, qreal y) |
|
491 \since 4.2 |
|
492 |
|
493 Sets the x and y coordinate of the element at index \a index to \a |
|
494 x and \a y. |
|
495 */ |
|
496 |
|
497 /*### |
|
498 \fn QPainterPath &QPainterPath::operator +=(const QPainterPath &other) |
|
499 |
|
500 Appends the \a other painter path to this painter path and returns a |
|
501 reference to the result. |
|
502 */ |
|
503 |
|
504 /*! |
|
505 Constructs an empty QPainterPath object. |
|
506 */ |
|
507 QPainterPath::QPainterPath() |
|
508 : d_ptr(0) |
|
509 { |
|
510 } |
|
511 |
|
512 /*! |
|
513 \fn QPainterPath::QPainterPath(const QPainterPath &path) |
|
514 |
|
515 Creates a QPainterPath object that is a copy of the given \a path. |
|
516 |
|
517 \sa operator=() |
|
518 */ |
|
519 QPainterPath::QPainterPath(const QPainterPath &other) |
|
520 : d_ptr(other.d_ptr.data()) |
|
521 { |
|
522 if (d_ptr) |
|
523 d_ptr->ref.ref(); |
|
524 } |
|
525 |
|
526 /*! |
|
527 Creates a QPainterPath object with the given \a startPoint as its |
|
528 current position. |
|
529 */ |
|
530 |
|
531 QPainterPath::QPainterPath(const QPointF &startPoint) |
|
532 : d_ptr(new QPainterPathData) |
|
533 { |
|
534 Element e = { startPoint.x(), startPoint.y(), MoveToElement }; |
|
535 d_func()->elements << e; |
|
536 } |
|
537 |
|
538 /*! |
|
539 \internal |
|
540 */ |
|
541 void QPainterPath::detach_helper() |
|
542 { |
|
543 QPainterPathPrivate *data = new QPainterPathData(*d_func()); |
|
544 d_ptr.reset(data); |
|
545 } |
|
546 |
|
547 /*! |
|
548 \internal |
|
549 */ |
|
550 void QPainterPath::ensureData_helper() |
|
551 { |
|
552 QPainterPathPrivate *data = new QPainterPathData; |
|
553 data->elements.reserve(16); |
|
554 QPainterPath::Element e = { 0, 0, QPainterPath::MoveToElement }; |
|
555 data->elements << e; |
|
556 d_ptr.reset(data); |
|
557 Q_ASSERT(d_ptr != 0); |
|
558 } |
|
559 |
|
560 /*! |
|
561 \fn QPainterPath &QPainterPath::operator=(const QPainterPath &path) |
|
562 |
|
563 Assigns the given \a path to this painter path. |
|
564 |
|
565 \sa QPainterPath() |
|
566 */ |
|
567 QPainterPath &QPainterPath::operator=(const QPainterPath &other) |
|
568 { |
|
569 if (other.d_func() != d_func()) { |
|
570 QPainterPathPrivate *data = other.d_func(); |
|
571 if (data) |
|
572 data->ref.ref(); |
|
573 d_ptr.reset(data); |
|
574 } |
|
575 return *this; |
|
576 } |
|
577 |
|
578 /*! |
|
579 Destroys this QPainterPath object. |
|
580 */ |
|
581 QPainterPath::~QPainterPath() |
|
582 { |
|
583 } |
|
584 |
|
585 /*! |
|
586 Closes the current subpath by drawing a line to the beginning of |
|
587 the subpath, automatically starting a new path. The current point |
|
588 of the new path is (0, 0). |
|
589 |
|
590 If the subpath does not contain any elements, this function does |
|
591 nothing. |
|
592 |
|
593 \sa moveTo(), {QPainterPath#Composing a QPainterPath}{Composing |
|
594 a QPainterPath} |
|
595 */ |
|
596 void QPainterPath::closeSubpath() |
|
597 { |
|
598 #ifdef QPP_DEBUG |
|
599 printf("QPainterPath::closeSubpath()\n"); |
|
600 #endif |
|
601 if (isEmpty()) |
|
602 return; |
|
603 detach(); |
|
604 |
|
605 d_func()->close(); |
|
606 } |
|
607 |
|
608 /*! |
|
609 \fn void QPainterPath::moveTo(qreal x, qreal y) |
|
610 |
|
611 \overload |
|
612 |
|
613 Moves the current position to (\a{x}, \a{y}) and starts a new |
|
614 subpath, implicitly closing the previous path. |
|
615 */ |
|
616 |
|
617 /*! |
|
618 \fn void QPainterPath::moveTo(const QPointF &point) |
|
619 |
|
620 Moves the current point to the given \a point, implicitly starting |
|
621 a new subpath and closing the previous one. |
|
622 |
|
623 \sa closeSubpath(), {QPainterPath#Composing a |
|
624 QPainterPath}{Composing a QPainterPath} |
|
625 */ |
|
626 void QPainterPath::moveTo(const QPointF &p) |
|
627 { |
|
628 #ifdef QPP_DEBUG |
|
629 printf("QPainterPath::moveTo() (%.2f,%.2f)\n", p.x(), p.y()); |
|
630 #endif |
|
631 #ifndef QT_NO_DEBUG |
|
632 if (qt_is_nan(p.x()) || qt_is_nan(p.y())) |
|
633 qWarning("QPainterPath::moveTo: Adding point where x or y is NaN, results are undefined"); |
|
634 #endif |
|
635 ensureData(); |
|
636 detach(); |
|
637 |
|
638 QPainterPathData *d = d_func(); |
|
639 Q_ASSERT(!d->elements.isEmpty()); |
|
640 |
|
641 d->require_moveTo = false; |
|
642 |
|
643 if (d->elements.last().type == MoveToElement) { |
|
644 d->elements.last().x = p.x(); |
|
645 d->elements.last().y = p.y(); |
|
646 } else { |
|
647 Element elm = { p.x(), p.y(), MoveToElement }; |
|
648 d->elements.append(elm); |
|
649 } |
|
650 d->cStart = d->elements.size() - 1; |
|
651 } |
|
652 |
|
653 /*! |
|
654 \fn void QPainterPath::lineTo(qreal x, qreal y) |
|
655 |
|
656 \overload |
|
657 |
|
658 Draws a line from the current position to the point (\a{x}, |
|
659 \a{y}). |
|
660 */ |
|
661 |
|
662 /*! |
|
663 \fn void QPainterPath::lineTo(const QPointF &endPoint) |
|
664 |
|
665 Adds a straight line from the current position to the given \a |
|
666 endPoint. After the line is drawn, the current position is updated |
|
667 to be at the end point of the line. |
|
668 |
|
669 \sa addPolygon(), addRect(), {QPainterPath#Composing a |
|
670 QPainterPath}{Composing a QPainterPath} |
|
671 */ |
|
672 void QPainterPath::lineTo(const QPointF &p) |
|
673 { |
|
674 #ifdef QPP_DEBUG |
|
675 printf("QPainterPath::lineTo() (%.2f,%.2f)\n", p.x(), p.y()); |
|
676 #endif |
|
677 #ifndef QT_NO_DEBUG |
|
678 if (qt_is_nan(p.x()) || qt_is_nan(p.y())) |
|
679 qWarning("QPainterPath::lineTo: Adding point where x or y is NaN, results are undefined"); |
|
680 #endif |
|
681 ensureData(); |
|
682 detach(); |
|
683 |
|
684 QPainterPathData *d = d_func(); |
|
685 Q_ASSERT(!d->elements.isEmpty()); |
|
686 d->maybeMoveTo(); |
|
687 if (p == QPointF(d->elements.last())) |
|
688 return; |
|
689 Element elm = { p.x(), p.y(), LineToElement }; |
|
690 d->elements.append(elm); |
|
691 } |
|
692 |
|
693 /*! |
|
694 \fn void QPainterPath::cubicTo(qreal c1X, qreal c1Y, qreal c2X, |
|
695 qreal c2Y, qreal endPointX, qreal endPointY); |
|
696 |
|
697 \overload |
|
698 |
|
699 Adds a cubic Bezier curve between the current position and the end |
|
700 point (\a{endPointX}, \a{endPointY}) with control points specified |
|
701 by (\a{c1X}, \a{c1Y}) and (\a{c2X}, \a{c2Y}). |
|
702 */ |
|
703 |
|
704 /*! |
|
705 \fn void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &endPoint) |
|
706 |
|
707 Adds a cubic Bezier curve between the current position and the |
|
708 given \a endPoint using the control points specified by \a c1, and |
|
709 \a c2. |
|
710 |
|
711 After the curve is added, the current position is updated to be at |
|
712 the end point of the curve. |
|
713 |
|
714 \table 100% |
|
715 \row |
|
716 \o \inlineimage qpainterpath-cubicto.png |
|
717 \o |
|
718 \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 1 |
|
719 \endtable |
|
720 |
|
721 \sa quadTo(), {QPainterPath#Composing a QPainterPath}{Composing |
|
722 a QPainterPath} |
|
723 */ |
|
724 void QPainterPath::cubicTo(const QPointF &c1, const QPointF &c2, const QPointF &e) |
|
725 { |
|
726 #ifdef QPP_DEBUG |
|
727 printf("QPainterPath::cubicTo() (%.2f,%.2f), (%.2f,%.2f), (%.2f,%.2f)\n", |
|
728 c1.x(), c1.y(), c2.x(), c2.y(), e.x(), e.y()); |
|
729 #endif |
|
730 #ifndef QT_NO_DEBUG |
|
731 if (qt_is_nan(c1.x()) || qt_is_nan(c1.y()) || qt_is_nan(c2.x()) || qt_is_nan(c2.y()) |
|
732 || qt_is_nan(e.x()) || qt_is_nan(e.y())) |
|
733 qWarning("QPainterPath::cubicTo: Adding point where x or y is NaN, results are undefined"); |
|
734 #endif |
|
735 ensureData(); |
|
736 detach(); |
|
737 |
|
738 QPainterPathData *d = d_func(); |
|
739 Q_ASSERT(!d->elements.isEmpty()); |
|
740 |
|
741 |
|
742 // Abort on empty curve as a stroker cannot handle this and the |
|
743 // curve is irrelevant anyway. |
|
744 if (d->elements.last() == c1 && c1 == c2 && c2 == e) |
|
745 return; |
|
746 |
|
747 d->maybeMoveTo(); |
|
748 |
|
749 Element ce1 = { c1.x(), c1.y(), CurveToElement }; |
|
750 Element ce2 = { c2.x(), c2.y(), CurveToDataElement }; |
|
751 Element ee = { e.x(), e.y(), CurveToDataElement }; |
|
752 d->elements << ce1 << ce2 << ee; |
|
753 } |
|
754 |
|
755 /*! |
|
756 \fn void QPainterPath::quadTo(qreal cx, qreal cy, qreal endPointX, qreal endPointY); |
|
757 |
|
758 \overload |
|
759 |
|
760 Adds a quadratic Bezier curve between the current point and the endpoint |
|
761 (\a{endPointX}, \a{endPointY}) with the control point specified by |
|
762 (\a{cx}, \a{cy}). |
|
763 */ |
|
764 |
|
765 /*! |
|
766 \fn void QPainterPath::quadTo(const QPointF &c, const QPointF &endPoint) |
|
767 |
|
768 Adds a quadratic Bezier curve between the current position and the |
|
769 given \a endPoint with the control point specified by \a c. |
|
770 |
|
771 After the curve is added, the current point is updated to be at |
|
772 the end point of the curve. |
|
773 |
|
774 \sa cubicTo(), {QPainterPath#Composing a QPainterPath}{Composing a |
|
775 QPainterPath} |
|
776 */ |
|
777 void QPainterPath::quadTo(const QPointF &c, const QPointF &e) |
|
778 { |
|
779 #ifdef QPP_DEBUG |
|
780 printf("QPainterPath::quadTo() (%.2f,%.2f), (%.2f,%.2f)\n", |
|
781 c.x(), c.y(), e.x(), e.y()); |
|
782 #endif |
|
783 #ifndef QT_NO_DEBUG |
|
784 if (qt_is_nan(c.x()) || qt_is_nan(c.y()) || qt_is_nan(e.x()) || qt_is_nan(e.y())) |
|
785 qWarning("QPainterPath::quadTo: Adding point where x or y is NaN, results are undefined"); |
|
786 #endif |
|
787 ensureData(); |
|
788 detach(); |
|
789 |
|
790 Q_D(QPainterPath); |
|
791 Q_ASSERT(!d->elements.isEmpty()); |
|
792 const QPainterPath::Element &elm = d->elements.at(elementCount()-1); |
|
793 QPointF prev(elm.x, elm.y); |
|
794 |
|
795 // Abort on empty curve as a stroker cannot handle this and the |
|
796 // curve is irrelevant anyway. |
|
797 if (prev == c && c == e) |
|
798 return; |
|
799 |
|
800 QPointF c1((prev.x() + 2*c.x()) / 3, (prev.y() + 2*c.y()) / 3); |
|
801 QPointF c2((e.x() + 2*c.x()) / 3, (e.y() + 2*c.y()) / 3); |
|
802 cubicTo(c1, c2, e); |
|
803 } |
|
804 |
|
805 /*! |
|
806 \fn void QPainterPath::arcTo(qreal x, qreal y, qreal width, qreal |
|
807 height, qreal startAngle, qreal sweepLength) |
|
808 |
|
809 \overload |
|
810 |
|
811 Creates an arc that occupies the rectangle QRectF(\a x, \a y, \a |
|
812 width, \a height), beginning at the specified \a startAngle and |
|
813 extending \a sweepLength degrees counter-clockwise. |
|
814 |
|
815 */ |
|
816 |
|
817 /*! |
|
818 \fn void QPainterPath::arcTo(const QRectF &rectangle, qreal startAngle, qreal sweepLength) |
|
819 |
|
820 Creates an arc that occupies the given \a rectangle, beginning at |
|
821 the specified \a startAngle and extending \a sweepLength degrees |
|
822 counter-clockwise. |
|
823 |
|
824 Angles are specified in degrees. Clockwise arcs can be specified |
|
825 using negative angles. |
|
826 |
|
827 Note that this function connects the starting point of the arc to |
|
828 the current position if they are not already connected. After the |
|
829 arc has been added, the current position is the last point in |
|
830 arc. To draw a line back to the first point, use the |
|
831 closeSubpath() function. |
|
832 |
|
833 \table 100% |
|
834 \row |
|
835 \o \inlineimage qpainterpath-arcto.png |
|
836 \o |
|
837 \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 2 |
|
838 \endtable |
|
839 |
|
840 \sa arcMoveTo(), addEllipse(), QPainter::drawArc(), QPainter::drawPie(), |
|
841 {QPainterPath#Composing a QPainterPath}{Composing a |
|
842 QPainterPath} |
|
843 */ |
|
844 void QPainterPath::arcTo(const QRectF &rect, qreal startAngle, qreal sweepLength) |
|
845 { |
|
846 #ifdef QPP_DEBUG |
|
847 printf("QPainterPath::arcTo() (%.2f, %.2f, %.2f, %.2f, angle=%.2f, sweep=%.2f\n", |
|
848 rect.x(), rect.y(), rect.width(), rect.height(), startAngle, sweepLength); |
|
849 #endif |
|
850 #ifndef QT_NO_DEBUG |
|
851 if (qt_is_nan(rect.x()) || qt_is_nan(rect.y()) || qt_is_nan(rect.width()) || qt_is_nan(rect.height()) |
|
852 || qt_is_nan(startAngle) || qt_is_nan(sweepLength)) |
|
853 qWarning("QPainterPath::arcTo: Adding arc where a parameter is NaN, results are undefined"); |
|
854 #endif |
|
855 if (rect.isNull()) |
|
856 return; |
|
857 |
|
858 ensureData(); |
|
859 detach(); |
|
860 |
|
861 int point_count; |
|
862 QPointF pts[15]; |
|
863 QPointF curve_start = qt_curves_for_arc(rect, startAngle, sweepLength, pts, &point_count); |
|
864 |
|
865 lineTo(curve_start); |
|
866 for (int i=0; i<point_count; i+=3) { |
|
867 cubicTo(pts[i].x(), pts[i].y(), |
|
868 pts[i+1].x(), pts[i+1].y(), |
|
869 pts[i+2].x(), pts[i+2].y()); |
|
870 } |
|
871 |
|
872 } |
|
873 |
|
874 |
|
875 /*! |
|
876 \fn void QPainterPath::arcMoveTo(qreal x, qreal y, qreal width, qreal height, qreal angle) |
|
877 \overload |
|
878 \since 4.2 |
|
879 |
|
880 Creates a move to that lies on the arc that occupies the |
|
881 QRectF(\a x, \a y, \a width, \a height) at \a angle. |
|
882 */ |
|
883 |
|
884 |
|
885 /*! |
|
886 \fn void QPainterPath::arcMoveTo(const QRectF &rectangle, qreal angle) |
|
887 \since 4.2 |
|
888 |
|
889 Creates a move to that lies on the arc that occupies the given \a |
|
890 rectangle at \a angle. |
|
891 |
|
892 Angles are specified in degrees. Clockwise arcs can be specified |
|
893 using negative angles. |
|
894 |
|
895 \sa moveTo(), arcTo() |
|
896 */ |
|
897 |
|
898 void QPainterPath::arcMoveTo(const QRectF &rect, qreal angle) |
|
899 { |
|
900 if (rect.isNull()) |
|
901 return; |
|
902 |
|
903 QPointF pt; |
|
904 qt_find_ellipse_coords(rect, angle, 0, &pt, 0); |
|
905 moveTo(pt); |
|
906 } |
|
907 |
|
908 |
|
909 |
|
910 /*! |
|
911 \fn QPointF QPainterPath::currentPosition() const |
|
912 |
|
913 Returns the current position of the path. |
|
914 */ |
|
915 QPointF QPainterPath::currentPosition() const |
|
916 { |
|
917 return !d_ptr || d_func()->elements.isEmpty() |
|
918 ? QPointF() |
|
919 : QPointF(d_func()->elements.last().x, d_func()->elements.last().y); |
|
920 } |
|
921 |
|
922 |
|
923 /*! |
|
924 \fn void QPainterPath::addRect(qreal x, qreal y, qreal width, qreal height) |
|
925 |
|
926 \overload |
|
927 |
|
928 Adds a rectangle at position (\a{x}, \a{y}), with the given \a |
|
929 width and \a height, as a closed subpath. |
|
930 */ |
|
931 |
|
932 /*! |
|
933 \fn void QPainterPath::addRect(const QRectF &rectangle) |
|
934 |
|
935 Adds the given \a rectangle to this path as a closed subpath. |
|
936 |
|
937 The \a rectangle is added as a clockwise set of lines. The painter |
|
938 path's current position after the \a rectangle has been added is |
|
939 at the top-left corner of the rectangle. |
|
940 |
|
941 \table 100% |
|
942 \row |
|
943 \o \inlineimage qpainterpath-addrectangle.png |
|
944 \o |
|
945 \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 3 |
|
946 \endtable |
|
947 |
|
948 \sa addRegion(), lineTo(), {QPainterPath#Composing a |
|
949 QPainterPath}{Composing a QPainterPath} |
|
950 */ |
|
951 void QPainterPath::addRect(const QRectF &r) |
|
952 { |
|
953 #ifndef QT_NO_DEBUG |
|
954 if (qt_is_nan(r.x()) || qt_is_nan(r.y()) || qt_is_nan(r.width()) || qt_is_nan(r.height())) |
|
955 qWarning("QPainterPath::addRect: Adding rect where a parameter is NaN, results are undefined"); |
|
956 #endif |
|
957 if (r.isNull()) |
|
958 return; |
|
959 |
|
960 ensureData(); |
|
961 detach(); |
|
962 |
|
963 d_func()->elements.reserve(d_func()->elements.size() + 5); |
|
964 moveTo(r.x(), r.y()); |
|
965 |
|
966 Element l1 = { r.x() + r.width(), r.y(), LineToElement }; |
|
967 Element l2 = { r.x() + r.width(), r.y() + r.height(), LineToElement }; |
|
968 Element l3 = { r.x(), r.y() + r.height(), LineToElement }; |
|
969 Element l4 = { r.x(), r.y(), LineToElement }; |
|
970 |
|
971 d_func()->elements << l1 << l2 << l3 << l4; |
|
972 d_func()->require_moveTo = true; |
|
973 } |
|
974 |
|
975 /*! |
|
976 Adds the given \a polygon to the path as an (unclosed) subpath. |
|
977 |
|
978 Note that the current position after the polygon has been added, |
|
979 is the last point in \a polygon. To draw a line back to the first |
|
980 point, use the closeSubpath() function. |
|
981 |
|
982 \table 100% |
|
983 \row |
|
984 \o \inlineimage qpainterpath-addpolygon.png |
|
985 \o |
|
986 \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 4 |
|
987 \endtable |
|
988 |
|
989 \sa lineTo(), {QPainterPath#Composing a QPainterPath}{Composing |
|
990 a QPainterPath} |
|
991 */ |
|
992 void QPainterPath::addPolygon(const QPolygonF &polygon) |
|
993 { |
|
994 if (polygon.isEmpty()) |
|
995 return; |
|
996 |
|
997 ensureData(); |
|
998 detach(); |
|
999 |
|
1000 d_func()->elements.reserve(d_func()->elements.size() + polygon.size()); |
|
1001 |
|
1002 moveTo(polygon.first()); |
|
1003 for (int i=1; i<polygon.size(); ++i) { |
|
1004 Element elm = { polygon.at(i).x(), polygon.at(i).y(), LineToElement }; |
|
1005 d_func()->elements << elm; |
|
1006 } |
|
1007 } |
|
1008 |
|
1009 /*! |
|
1010 \fn void QPainterPath::addEllipse(const QRectF &boundingRectangle) |
|
1011 |
|
1012 Creates an ellipse within the specified \a boundingRectangle |
|
1013 and adds it to the painter path as a closed subpath. |
|
1014 |
|
1015 The ellipse is composed of a clockwise curve, starting and |
|
1016 finishing at zero degrees (the 3 o'clock position). |
|
1017 |
|
1018 \table 100% |
|
1019 \row |
|
1020 \o \inlineimage qpainterpath-addellipse.png |
|
1021 \o |
|
1022 \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 5 |
|
1023 \endtable |
|
1024 |
|
1025 \sa arcTo(), QPainter::drawEllipse(), {QPainterPath#Composing a |
|
1026 QPainterPath}{Composing a QPainterPath} |
|
1027 */ |
|
1028 void QPainterPath::addEllipse(const QRectF &boundingRect) |
|
1029 { |
|
1030 #ifndef QT_NO_DEBUG |
|
1031 if (qt_is_nan(boundingRect.x()) || qt_is_nan(boundingRect.y()) |
|
1032 || qt_is_nan(boundingRect.width()) || qt_is_nan(boundingRect.height())) |
|
1033 qWarning("QPainterPath::addEllipse: Adding ellipse where a parameter is NaN, results are undefined"); |
|
1034 #endif |
|
1035 if (boundingRect.isNull()) |
|
1036 return; |
|
1037 |
|
1038 ensureData(); |
|
1039 detach(); |
|
1040 |
|
1041 Q_D(QPainterPath); |
|
1042 d->elements.reserve(d->elements.size() + 13); |
|
1043 |
|
1044 QPointF pts[12]; |
|
1045 int point_count; |
|
1046 QPointF start = qt_curves_for_arc(boundingRect, 0, -360, pts, &point_count); |
|
1047 |
|
1048 moveTo(start); |
|
1049 cubicTo(pts[0], pts[1], pts[2]); // 0 -> 270 |
|
1050 cubicTo(pts[3], pts[4], pts[5]); // 270 -> 180 |
|
1051 cubicTo(pts[6], pts[7], pts[8]); // 180 -> 90 |
|
1052 cubicTo(pts[9], pts[10], pts[11]); // 90 - >0 |
|
1053 d_func()->require_moveTo = true; |
|
1054 } |
|
1055 |
|
1056 /*! |
|
1057 \fn void QPainterPath::addText(const QPointF &point, const QFont &font, const QString &text) |
|
1058 |
|
1059 Adds the given \a text to this path as a set of closed subpaths |
|
1060 created from the \a font supplied. The subpaths are positioned so |
|
1061 that the left end of the text's baseline lies at the specified \a |
|
1062 point. |
|
1063 |
|
1064 \table 100% |
|
1065 \row |
|
1066 \o \inlineimage qpainterpath-addtext.png |
|
1067 \o |
|
1068 \snippet doc/src/snippets/code/src_gui_painting_qpainterpath.cpp 6 |
|
1069 \endtable |
|
1070 |
|
1071 \sa QPainter::drawText(), {QPainterPath#Composing a |
|
1072 QPainterPath}{Composing a QPainterPath} |
|
1073 */ |
|
1074 void QPainterPath::addText(const QPointF &point, const QFont &f, const QString &text) |
|
1075 { |
|
1076 if (text.isEmpty()) |
|
1077 return; |
|
1078 |
|
1079 ensureData(); |
|
1080 detach(); |
|
1081 |
|
1082 QTextLayout layout(text, f); |
|
1083 layout.setCacheEnabled(true); |
|
1084 QTextEngine *eng = layout.engine(); |
|
1085 layout.beginLayout(); |
|
1086 QTextLine line = layout.createLine(); |
|
1087 layout.endLayout(); |
|
1088 const QScriptLine &sl = eng->lines[0]; |
|
1089 if (!sl.length || !eng->layoutData) |
|
1090 return; |
|
1091 |
|
1092 int nItems = eng->layoutData->items.size(); |
|
1093 |
|
1094 qreal x(point.x()); |
|
1095 qreal y(point.y()); |
|
1096 |
|
1097 QVarLengthArray<int> visualOrder(nItems); |
|
1098 QVarLengthArray<uchar> levels(nItems); |
|
1099 for (int i = 0; i < nItems; ++i) |
|
1100 levels[i] = eng->layoutData->items[i].analysis.bidiLevel; |
|
1101 QTextEngine::bidiReorder(nItems, levels.data(), visualOrder.data()); |
|
1102 |
|
1103 for (int i = 0; i < nItems; ++i) { |
|
1104 int item = visualOrder[i]; |
|
1105 QScriptItem &si = eng->layoutData->items[item]; |
|
1106 |
|
1107 if (si.analysis.flags < QScriptAnalysis::TabOrObject) { |
|
1108 QGlyphLayout glyphs = eng->shapedGlyphs(&si); |
|
1109 QFontEngine *fe = f.d->engineForScript(si.analysis.script); |
|
1110 Q_ASSERT(fe); |
|
1111 fe->addOutlineToPath(x, y, glyphs, this, |
|
1112 si.analysis.bidiLevel % 2 |
|
1113 ? QTextItem::RenderFlags(QTextItem::RightToLeft) |
|
1114 : QTextItem::RenderFlags(0)); |
|
1115 |
|
1116 const qreal lw = fe->lineThickness().toReal(); |
|
1117 if (f.d->underline) { |
|
1118 qreal pos = fe->underlinePosition().toReal(); |
|
1119 addRect(x, y + pos, si.width.toReal(), lw); |
|
1120 } |
|
1121 if (f.d->overline) { |
|
1122 qreal pos = fe->ascent().toReal() + 1; |
|
1123 addRect(x, y - pos, si.width.toReal(), lw); |
|
1124 } |
|
1125 if (f.d->strikeOut) { |
|
1126 qreal pos = fe->ascent().toReal() / 3; |
|
1127 addRect(x, y - pos, si.width.toReal(), lw); |
|
1128 } |
|
1129 } |
|
1130 x += si.width.toReal(); |
|
1131 } |
|
1132 } |
|
1133 |
|
1134 /*! |
|
1135 \fn void QPainterPath::addPath(const QPainterPath &path) |
|
1136 |
|
1137 Adds the given \a path to \e this path as a closed subpath. |
|
1138 |
|
1139 \sa connectPath(), {QPainterPath#Composing a |
|
1140 QPainterPath}{Composing a QPainterPath} |
|
1141 */ |
|
1142 void QPainterPath::addPath(const QPainterPath &other) |
|
1143 { |
|
1144 if (other.isEmpty()) |
|
1145 return; |
|
1146 |
|
1147 ensureData(); |
|
1148 detach(); |
|
1149 |
|
1150 QPainterPathData *d = reinterpret_cast<QPainterPathData *>(d_func()); |
|
1151 // Remove last moveto so we don't get multiple moveto's |
|
1152 if (d->elements.last().type == MoveToElement) |
|
1153 d->elements.remove(d->elements.size()-1); |
|
1154 |
|
1155 // Locate where our own current subpath will start after the other path is added. |
|
1156 int cStart = d->elements.size() + other.d_func()->cStart; |
|
1157 d->elements += other.d_func()->elements; |
|
1158 d->cStart = cStart; |
|
1159 |
|
1160 d->require_moveTo = other.d_func()->isClosed(); |
|
1161 } |
|
1162 |
|
1163 |
|
1164 /*! |
|
1165 \fn void QPainterPath::connectPath(const QPainterPath &path) |
|
1166 |
|
1167 Connects the given \a path to \e this path by adding a line from the |
|
1168 last element of this path to the first element of the given path. |
|
1169 |
|
1170 \sa addPath(), {QPainterPath#Composing a QPainterPath}{Composing |
|
1171 a QPainterPath} |
|
1172 */ |
|
1173 void QPainterPath::connectPath(const QPainterPath &other) |
|
1174 { |
|
1175 if (other.isEmpty()) |
|
1176 return; |
|
1177 |
|
1178 ensureData(); |
|
1179 detach(); |
|
1180 |
|
1181 QPainterPathData *d = reinterpret_cast<QPainterPathData *>(d_func()); |
|
1182 // Remove last moveto so we don't get multiple moveto's |
|
1183 if (d->elements.last().type == MoveToElement) |
|
1184 d->elements.remove(d->elements.size()-1); |
|
1185 |
|
1186 // Locate where our own current subpath will start after the other path is added. |
|
1187 int cStart = d->elements.size() + other.d_func()->cStart; |
|
1188 int first = d->elements.size(); |
|
1189 d->elements += other.d_func()->elements; |
|
1190 |
|
1191 d->elements[first].type = LineToElement; |
|
1192 |
|
1193 // avoid duplicate points |
|
1194 if (first > 0 && QPointF(d->elements[first]) == QPointF(d->elements[first - 1])) { |
|
1195 d->elements.remove(first--); |
|
1196 --cStart; |
|
1197 } |
|
1198 |
|
1199 if (cStart != first) |
|
1200 d->cStart = cStart; |
|
1201 } |
|
1202 |
|
1203 /*! |
|
1204 Adds the given \a region to the path by adding each rectangle in |
|
1205 the region as a separate closed subpath. |
|
1206 |
|
1207 \sa addRect(), {QPainterPath#Composing a QPainterPath}{Composing |
|
1208 a QPainterPath} |
|
1209 */ |
|
1210 void QPainterPath::addRegion(const QRegion ®ion) |
|
1211 { |
|
1212 ensureData(); |
|
1213 detach(); |
|
1214 |
|
1215 QVector<QRect> rects = region.rects(); |
|
1216 d_func()->elements.reserve(rects.size() * 5); |
|
1217 for (int i=0; i<rects.size(); ++i) |
|
1218 addRect(rects.at(i)); |
|
1219 } |
|
1220 |
|
1221 |
|
1222 /*! |
|
1223 Returns the painter path's currently set fill rule. |
|
1224 |
|
1225 \sa setFillRule() |
|
1226 */ |
|
1227 Qt::FillRule QPainterPath::fillRule() const |
|
1228 { |
|
1229 return isEmpty() ? Qt::OddEvenFill : d_func()->fillRule; |
|
1230 } |
|
1231 |
|
1232 /*! |
|
1233 \fn void QPainterPath::setFillRule(Qt::FillRule fillRule) |
|
1234 |
|
1235 Sets the fill rule of the painter path to the given \a |
|
1236 fillRule. Qt provides two methods for filling paths: |
|
1237 |
|
1238 \table |
|
1239 \row |
|
1240 \o \inlineimage qt-fillrule-oddeven.png |
|
1241 \o \inlineimage qt-fillrule-winding.png |
|
1242 \header |
|
1243 \o Qt::OddEvenFill (default) |
|
1244 \o Qt::WindingFill |
|
1245 \endtable |
|
1246 |
|
1247 \sa fillRule() |
|
1248 */ |
|
1249 void QPainterPath::setFillRule(Qt::FillRule fillRule) |
|
1250 { |
|
1251 ensureData(); |
|
1252 detach(); |
|
1253 |
|
1254 d_func()->fillRule = fillRule; |
|
1255 } |
|
1256 |
|
1257 #define QT_BEZIER_A(bezier, coord) 3 * (-bezier.coord##1 \ |
|
1258 + 3*bezier.coord##2 \ |
|
1259 - 3*bezier.coord##3 \ |
|
1260 +bezier.coord##4) |
|
1261 |
|
1262 #define QT_BEZIER_B(bezier, coord) 6 * (bezier.coord##1 \ |
|
1263 - 2*bezier.coord##2 \ |
|
1264 + bezier.coord##3) |
|
1265 |
|
1266 #define QT_BEZIER_C(bezier, coord) 3 * (- bezier.coord##1 \ |
|
1267 + bezier.coord##2) |
|
1268 |
|
1269 #define QT_BEZIER_CHECK_T(bezier, t) \ |
|
1270 if (t >= 0 && t <= 1) { \ |
|
1271 QPointF p(b.pointAt(t)); \ |
|
1272 if (p.x() < minx) minx = p.x(); \ |
|
1273 else if (p.x() > maxx) maxx = p.x(); \ |
|
1274 if (p.y() < miny) miny = p.y(); \ |
|
1275 else if (p.y() > maxy) maxy = p.y(); \ |
|
1276 } |
|
1277 |
|
1278 |
|
1279 static QRectF qt_painterpath_bezier_extrema(const QBezier &b) |
|
1280 { |
|
1281 qreal minx, miny, maxx, maxy; |
|
1282 |
|
1283 // initialize with end points |
|
1284 if (b.x1 < b.x4) { |
|
1285 minx = b.x1; |
|
1286 maxx = b.x4; |
|
1287 } else { |
|
1288 minx = b.x4; |
|
1289 maxx = b.x1; |
|
1290 } |
|
1291 if (b.y1 < b.y4) { |
|
1292 miny = b.y1; |
|
1293 maxy = b.y4; |
|
1294 } else { |
|
1295 miny = b.y4; |
|
1296 maxy = b.y1; |
|
1297 } |
|
1298 |
|
1299 // Update for the X extrema |
|
1300 { |
|
1301 qreal ax = QT_BEZIER_A(b, x); |
|
1302 qreal bx = QT_BEZIER_B(b, x); |
|
1303 qreal cx = QT_BEZIER_C(b, x); |
|
1304 // specialcase quadratic curves to avoid div by zero |
|
1305 if (qFuzzyIsNull(ax)) { |
|
1306 |
|
1307 // linear curves are covered by initialization. |
|
1308 if (!qFuzzyIsNull(bx)) { |
|
1309 qreal t = -cx / bx; |
|
1310 QT_BEZIER_CHECK_T(b, t); |
|
1311 } |
|
1312 |
|
1313 } else { |
|
1314 const qreal tx = bx * bx - 4 * ax * cx; |
|
1315 |
|
1316 if (tx >= 0) { |
|
1317 qreal temp = qSqrt(tx); |
|
1318 qreal rcp = 1 / (2 * ax); |
|
1319 qreal t1 = (-bx + temp) * rcp; |
|
1320 QT_BEZIER_CHECK_T(b, t1); |
|
1321 |
|
1322 qreal t2 = (-bx - temp) * rcp; |
|
1323 QT_BEZIER_CHECK_T(b, t2); |
|
1324 } |
|
1325 } |
|
1326 } |
|
1327 |
|
1328 // Update for the Y extrema |
|
1329 { |
|
1330 qreal ay = QT_BEZIER_A(b, y); |
|
1331 qreal by = QT_BEZIER_B(b, y); |
|
1332 qreal cy = QT_BEZIER_C(b, y); |
|
1333 |
|
1334 // specialcase quadratic curves to avoid div by zero |
|
1335 if (qFuzzyIsNull(ay)) { |
|
1336 |
|
1337 // linear curves are covered by initialization. |
|
1338 if (!qFuzzyIsNull(by)) { |
|
1339 qreal t = -cy / by; |
|
1340 QT_BEZIER_CHECK_T(b, t); |
|
1341 } |
|
1342 |
|
1343 } else { |
|
1344 const qreal ty = by * by - 4 * ay * cy; |
|
1345 |
|
1346 if (ty > 0) { |
|
1347 qreal temp = qSqrt(ty); |
|
1348 qreal rcp = 1 / (2 * ay); |
|
1349 qreal t1 = (-by + temp) * rcp; |
|
1350 QT_BEZIER_CHECK_T(b, t1); |
|
1351 |
|
1352 qreal t2 = (-by - temp) * rcp; |
|
1353 QT_BEZIER_CHECK_T(b, t2); |
|
1354 } |
|
1355 } |
|
1356 } |
|
1357 return QRectF(minx, miny, maxx - minx, maxy - miny); |
|
1358 } |
|
1359 |
|
1360 /*! |
|
1361 Returns the bounding rectangle of this painter path as a rectangle with |
|
1362 floating point precision. |
|
1363 |
|
1364 \sa controlPointRect() |
|
1365 */ |
|
1366 QRectF QPainterPath::boundingRect() const |
|
1367 { |
|
1368 if (!d_ptr) |
|
1369 return QRectF(); |
|
1370 QPainterPathData *d = d_func(); |
|
1371 |
|
1372 if (d->dirtyBounds) |
|
1373 computeBoundingRect(); |
|
1374 return d->bounds; |
|
1375 } |
|
1376 |
|
1377 /*! |
|
1378 Returns the rectangle containing all the points and control points |
|
1379 in this path. |
|
1380 |
|
1381 This function is significantly faster to compute than the exact |
|
1382 boundingRect(), and the returned rectangle is always a superset of |
|
1383 the rectangle returned by boundingRect(). |
|
1384 |
|
1385 \sa boundingRect() |
|
1386 */ |
|
1387 QRectF QPainterPath::controlPointRect() const |
|
1388 { |
|
1389 if (!d_ptr) |
|
1390 return QRectF(); |
|
1391 QPainterPathData *d = d_func(); |
|
1392 |
|
1393 if (d->dirtyControlBounds) |
|
1394 computeControlPointRect(); |
|
1395 return d->controlBounds; |
|
1396 } |
|
1397 |
|
1398 |
|
1399 /*! |
|
1400 \fn bool QPainterPath::isEmpty() const |
|
1401 |
|
1402 Returns true if either there are no elements in this path, or if the only |
|
1403 element is a MoveToElement; otherwise returns false. |
|
1404 |
|
1405 \sa elementCount() |
|
1406 */ |
|
1407 |
|
1408 /*! |
|
1409 Creates and returns a reversed copy of the path. |
|
1410 |
|
1411 It is the order of the elements that is reversed: If a |
|
1412 QPainterPath is composed by calling the moveTo(), lineTo() and |
|
1413 cubicTo() functions in the specified order, the reversed copy is |
|
1414 composed by calling cubicTo(), lineTo() and moveTo(). |
|
1415 */ |
|
1416 QPainterPath QPainterPath::toReversed() const |
|
1417 { |
|
1418 Q_D(const QPainterPath); |
|
1419 QPainterPath rev; |
|
1420 |
|
1421 if (isEmpty()) { |
|
1422 rev = *this; |
|
1423 return rev; |
|
1424 } |
|
1425 |
|
1426 rev.moveTo(d->elements.at(d->elements.size()-1).x, d->elements.at(d->elements.size()-1).y); |
|
1427 |
|
1428 for (int i=d->elements.size()-1; i>=1; --i) { |
|
1429 const QPainterPath::Element &elm = d->elements.at(i); |
|
1430 const QPainterPath::Element &prev = d->elements.at(i-1); |
|
1431 switch (elm.type) { |
|
1432 case LineToElement: |
|
1433 rev.lineTo(prev.x, prev.y); |
|
1434 break; |
|
1435 case MoveToElement: |
|
1436 rev.moveTo(prev.x, prev.y); |
|
1437 break; |
|
1438 case CurveToDataElement: |
|
1439 { |
|
1440 Q_ASSERT(i>=3); |
|
1441 const QPainterPath::Element &cp1 = d->elements.at(i-2); |
|
1442 const QPainterPath::Element &sp = d->elements.at(i-3); |
|
1443 Q_ASSERT(prev.type == CurveToDataElement); |
|
1444 Q_ASSERT(cp1.type == CurveToElement); |
|
1445 rev.cubicTo(prev.x, prev.y, cp1.x, cp1.y, sp.x, sp.y); |
|
1446 i -= 2; |
|
1447 break; |
|
1448 } |
|
1449 default: |
|
1450 Q_ASSERT(!"qt_reversed_path"); |
|
1451 break; |
|
1452 } |
|
1453 } |
|
1454 //qt_debug_path(rev); |
|
1455 return rev; |
|
1456 } |
|
1457 |
|
1458 /*! |
|
1459 Converts the path into a list of polygons using the QTransform |
|
1460 \a matrix, and returns the list. |
|
1461 |
|
1462 This function creates one polygon for each subpath regardless of |
|
1463 intersecting subpaths (i.e. overlapping bounding rectangles). To |
|
1464 make sure that such overlapping subpaths are filled correctly, use |
|
1465 the toFillPolygons() function instead. |
|
1466 |
|
1467 \sa toFillPolygons(), toFillPolygon(), {QPainterPath#QPainterPath |
|
1468 Conversion}{QPainterPath Conversion} |
|
1469 */ |
|
1470 QList<QPolygonF> QPainterPath::toSubpathPolygons(const QTransform &matrix) const |
|
1471 { |
|
1472 |
|
1473 Q_D(const QPainterPath); |
|
1474 QList<QPolygonF> flatCurves; |
|
1475 if (isEmpty()) |
|
1476 return flatCurves; |
|
1477 |
|
1478 QPolygonF current; |
|
1479 for (int i=0; i<elementCount(); ++i) { |
|
1480 const QPainterPath::Element &e = d->elements.at(i); |
|
1481 switch (e.type) { |
|
1482 case QPainterPath::MoveToElement: |
|
1483 if (current.size() > 1) |
|
1484 flatCurves += current; |
|
1485 current.clear(); |
|
1486 current.reserve(16); |
|
1487 current += QPointF(e.x, e.y) * matrix; |
|
1488 break; |
|
1489 case QPainterPath::LineToElement: |
|
1490 current += QPointF(e.x, e.y) * matrix; |
|
1491 break; |
|
1492 case QPainterPath::CurveToElement: { |
|
1493 Q_ASSERT(d->elements.at(i+1).type == QPainterPath::CurveToDataElement); |
|
1494 Q_ASSERT(d->elements.at(i+2).type == QPainterPath::CurveToDataElement); |
|
1495 QBezier bezier = QBezier::fromPoints(QPointF(d->elements.at(i-1).x, d->elements.at(i-1).y) * matrix, |
|
1496 QPointF(e.x, e.y) * matrix, |
|
1497 QPointF(d->elements.at(i+1).x, d->elements.at(i+1).y) * matrix, |
|
1498 QPointF(d->elements.at(i+2).x, d->elements.at(i+2).y) * matrix); |
|
1499 bezier.addToPolygon(¤t); |
|
1500 i+=2; |
|
1501 break; |
|
1502 } |
|
1503 case QPainterPath::CurveToDataElement: |
|
1504 Q_ASSERT(!"QPainterPath::toSubpathPolygons(), bad element type"); |
|
1505 break; |
|
1506 } |
|
1507 } |
|
1508 |
|
1509 if (current.size()>1) |
|
1510 flatCurves += current; |
|
1511 |
|
1512 return flatCurves; |
|
1513 } |
|
1514 |
|
1515 /*! |
|
1516 \overload |
|
1517 */ |
|
1518 QList<QPolygonF> QPainterPath::toSubpathPolygons(const QMatrix &matrix) const |
|
1519 { |
|
1520 return toSubpathPolygons(QTransform(matrix)); |
|
1521 } |
|
1522 |
|
1523 /*! |
|
1524 Converts the path into a list of polygons using the |
|
1525 QTransform \a matrix, and returns the list. |
|
1526 |
|
1527 The function differs from the toFillPolygon() function in that it |
|
1528 creates several polygons. It is provided because it is usually |
|
1529 faster to draw several small polygons than to draw one large |
|
1530 polygon, even though the total number of points drawn is the same. |
|
1531 |
|
1532 The toFillPolygons() function differs from the toSubpathPolygons() |
|
1533 function in that it create only polygon for subpaths that have |
|
1534 overlapping bounding rectangles. |
|
1535 |
|
1536 Like the toFillPolygon() function, this function uses a rewinding |
|
1537 technique to make sure that overlapping subpaths can be filled |
|
1538 using the correct fill rule. Note that rewinding inserts addition |
|
1539 lines in the polygons so the outline of the fill polygon does not |
|
1540 match the outline of the path. |
|
1541 |
|
1542 \sa toSubpathPolygons(), toFillPolygon(), |
|
1543 {QPainterPath#QPainterPath Conversion}{QPainterPath Conversion} |
|
1544 */ |
|
1545 QList<QPolygonF> QPainterPath::toFillPolygons(const QTransform &matrix) const |
|
1546 { |
|
1547 |
|
1548 QList<QPolygonF> polys; |
|
1549 |
|
1550 QList<QPolygonF> subpaths = toSubpathPolygons(matrix); |
|
1551 int count = subpaths.size(); |
|
1552 |
|
1553 if (count == 0) |
|
1554 return polys; |
|
1555 |
|
1556 QList<QRectF> bounds; |
|
1557 for (int i=0; i<count; ++i) |
|
1558 bounds += subpaths.at(i).boundingRect(); |
|
1559 |
|
1560 #ifdef QPP_FILLPOLYGONS_DEBUG |
|
1561 printf("QPainterPath::toFillPolygons, subpathCount=%d\n", count); |
|
1562 for (int i=0; i<bounds.size(); ++i) |
|
1563 qDebug() << " bounds" << i << bounds.at(i); |
|
1564 #endif |
|
1565 |
|
1566 QVector< QList<int> > isects; |
|
1567 isects.resize(count); |
|
1568 |
|
1569 // find all intersections |
|
1570 for (int j=0; j<count; ++j) { |
|
1571 if (subpaths.at(j).size() <= 2) |
|
1572 continue; |
|
1573 QRectF cbounds = bounds.at(j); |
|
1574 for (int i=0; i<count; ++i) { |
|
1575 if (cbounds.intersects(bounds.at(i))) { |
|
1576 isects[j] << i; |
|
1577 } |
|
1578 } |
|
1579 } |
|
1580 |
|
1581 #ifdef QPP_FILLPOLYGONS_DEBUG |
|
1582 printf("Intersections before flattening:\n"); |
|
1583 for (int i = 0; i < count; ++i) { |
|
1584 printf("%d: ", i); |
|
1585 for (int j = 0; j < isects[i].size(); ++j) { |
|
1586 printf("%d ", isects[i][j]); |
|
1587 } |
|
1588 printf("\n"); |
|
1589 } |
|
1590 #endif |
|
1591 |
|
1592 // flatten the sets of intersections |
|
1593 for (int i=0; i<count; ++i) { |
|
1594 const QList<int> ¤t_isects = isects.at(i); |
|
1595 for (int j=0; j<current_isects.size(); ++j) { |
|
1596 int isect_j = current_isects.at(j); |
|
1597 if (isect_j == i) |
|
1598 continue; |
|
1599 for (int k=0; k<isects[isect_j].size(); ++k) { |
|
1600 int isect_k = isects[isect_j][k]; |
|
1601 if (isect_k != i && !isects.at(i).contains(isect_k)) { |
|
1602 isects[i] += isect_k; |
|
1603 } |
|
1604 } |
|
1605 isects[isect_j].clear(); |
|
1606 } |
|
1607 } |
|
1608 |
|
1609 #ifdef QPP_FILLPOLYGONS_DEBUG |
|
1610 printf("Intersections after flattening:\n"); |
|
1611 for (int i = 0; i < count; ++i) { |
|
1612 printf("%d: ", i); |
|
1613 for (int j = 0; j < isects[i].size(); ++j) { |
|
1614 printf("%d ", isects[i][j]); |
|
1615 } |
|
1616 printf("\n"); |
|
1617 } |
|
1618 #endif |
|
1619 |
|
1620 // Join the intersected subpaths as rewinded polygons |
|
1621 for (int i=0; i<count; ++i) { |
|
1622 const QList<int> &subpath_list = isects[i]; |
|
1623 if (!subpath_list.isEmpty()) { |
|
1624 QPolygonF buildUp; |
|
1625 for (int j=0; j<subpath_list.size(); ++j) { |
|
1626 const QPolygonF &subpath = subpaths.at(subpath_list.at(j)); |
|
1627 buildUp += subpath; |
|
1628 if (!subpath.isClosed()) |
|
1629 buildUp += subpath.first(); |
|
1630 if (!buildUp.isClosed()) |
|
1631 buildUp += buildUp.first(); |
|
1632 } |
|
1633 polys += buildUp; |
|
1634 } |
|
1635 } |
|
1636 |
|
1637 return polys; |
|
1638 } |
|
1639 |
|
1640 /*! |
|
1641 \overload |
|
1642 */ |
|
1643 QList<QPolygonF> QPainterPath::toFillPolygons(const QMatrix &matrix) const |
|
1644 { |
|
1645 return toFillPolygons(QTransform(matrix)); |
|
1646 } |
|
1647 |
|
1648 //same as qt_polygon_isect_line in qpolygon.cpp |
|
1649 static void qt_painterpath_isect_line(const QPointF &p1, |
|
1650 const QPointF &p2, |
|
1651 const QPointF &pos, |
|
1652 int *winding) |
|
1653 { |
|
1654 qreal x1 = p1.x(); |
|
1655 qreal y1 = p1.y(); |
|
1656 qreal x2 = p2.x(); |
|
1657 qreal y2 = p2.y(); |
|
1658 qreal y = pos.y(); |
|
1659 |
|
1660 int dir = 1; |
|
1661 |
|
1662 if (qFuzzyCompare(y1, y2)) { |
|
1663 // ignore horizontal lines according to scan conversion rule |
|
1664 return; |
|
1665 } else if (y2 < y1) { |
|
1666 qreal x_tmp = x2; x2 = x1; x1 = x_tmp; |
|
1667 qreal y_tmp = y2; y2 = y1; y1 = y_tmp; |
|
1668 dir = -1; |
|
1669 } |
|
1670 |
|
1671 if (y >= y1 && y < y2) { |
|
1672 qreal x = x1 + ((x2 - x1) / (y2 - y1)) * (y - y1); |
|
1673 |
|
1674 // count up the winding number if we're |
|
1675 if (x<=pos.x()) { |
|
1676 (*winding) += dir; |
|
1677 } |
|
1678 } |
|
1679 } |
|
1680 |
|
1681 static void qt_painterpath_isect_curve(const QBezier &bezier, const QPointF &pt, |
|
1682 int *winding) |
|
1683 { |
|
1684 qreal y = pt.y(); |
|
1685 qreal x = pt.x(); |
|
1686 QRectF bounds = bezier.bounds(); |
|
1687 |
|
1688 // potential intersection, divide and try again... |
|
1689 // Please note that a sideeffect of the bottom exclusion is that |
|
1690 // horizontal lines are dropped, but this is correct according to |
|
1691 // scan conversion rules. |
|
1692 if (y >= bounds.y() && y < bounds.y() + bounds.height()) { |
|
1693 |
|
1694 // hit lower limit... This is a rough threshold, but its a |
|
1695 // tradeoff between speed and precision. |
|
1696 const qreal lower_bound = qreal(.001); |
|
1697 if (bounds.width() < lower_bound && bounds.height() < lower_bound) { |
|
1698 // We make the assumption here that the curve starts to |
|
1699 // approximate a line after while (i.e. that it doesn't |
|
1700 // change direction drastically during its slope) |
|
1701 if (bezier.pt1().x() <= x) { |
|
1702 (*winding) += (bezier.pt4().y() > bezier.pt1().y() ? 1 : -1); |
|
1703 } |
|
1704 return; |
|
1705 } |
|
1706 |
|
1707 // split curve and try again... |
|
1708 QBezier first_half, second_half; |
|
1709 bezier.split(&first_half, &second_half); |
|
1710 qt_painterpath_isect_curve(first_half, pt, winding); |
|
1711 qt_painterpath_isect_curve(second_half, pt, winding); |
|
1712 } |
|
1713 } |
|
1714 |
|
1715 /*! |
|
1716 \fn bool QPainterPath::contains(const QPointF &point) const |
|
1717 |
|
1718 Returns true if the given \a point is inside the path, otherwise |
|
1719 returns false. |
|
1720 |
|
1721 \sa intersects() |
|
1722 */ |
|
1723 bool QPainterPath::contains(const QPointF &pt) const |
|
1724 { |
|
1725 if (isEmpty() || !controlPointRect().contains(pt)) |
|
1726 return false; |
|
1727 |
|
1728 QPainterPathData *d = d_func(); |
|
1729 |
|
1730 int winding_number = 0; |
|
1731 |
|
1732 QPointF last_pt; |
|
1733 QPointF last_start; |
|
1734 for (int i=0; i<d->elements.size(); ++i) { |
|
1735 const Element &e = d->elements.at(i); |
|
1736 |
|
1737 switch (e.type) { |
|
1738 |
|
1739 case MoveToElement: |
|
1740 if (i > 0) // implicitly close all paths. |
|
1741 qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number); |
|
1742 last_start = last_pt = e; |
|
1743 break; |
|
1744 |
|
1745 case LineToElement: |
|
1746 qt_painterpath_isect_line(last_pt, e, pt, &winding_number); |
|
1747 last_pt = e; |
|
1748 break; |
|
1749 |
|
1750 case CurveToElement: |
|
1751 { |
|
1752 const QPainterPath::Element &cp2 = d->elements.at(++i); |
|
1753 const QPainterPath::Element &ep = d->elements.at(++i); |
|
1754 qt_painterpath_isect_curve(QBezier::fromPoints(last_pt, e, cp2, ep), |
|
1755 pt, &winding_number); |
|
1756 last_pt = ep; |
|
1757 |
|
1758 } |
|
1759 break; |
|
1760 |
|
1761 default: |
|
1762 break; |
|
1763 } |
|
1764 } |
|
1765 |
|
1766 // implicitly close last subpath |
|
1767 if (last_pt != last_start) |
|
1768 qt_painterpath_isect_line(last_pt, last_start, pt, &winding_number); |
|
1769 |
|
1770 return (d->fillRule == Qt::WindingFill |
|
1771 ? (winding_number != 0) |
|
1772 : ((winding_number % 2) != 0)); |
|
1773 } |
|
1774 |
|
1775 static bool qt_painterpath_isect_line_rect(qreal x1, qreal y1, qreal x2, qreal y2, |
|
1776 const QRectF &rect) |
|
1777 { |
|
1778 qreal left = rect.left(); |
|
1779 qreal right = rect.right(); |
|
1780 qreal top = rect.top(); |
|
1781 qreal bottom = rect.bottom(); |
|
1782 |
|
1783 enum { Left, Right, Top, Bottom }; |
|
1784 // clip the lines, after cohen-sutherland, see e.g. http://www.nondot.org/~sabre/graphpro/line6.html |
|
1785 int p1 = ((x1 < left) << Left) |
|
1786 | ((x1 > right) << Right) |
|
1787 | ((y1 < top) << Top) |
|
1788 | ((y1 > bottom) << Bottom); |
|
1789 int p2 = ((x2 < left) << Left) |
|
1790 | ((x2 > right) << Right) |
|
1791 | ((y2 < top) << Top) |
|
1792 | ((y2 > bottom) << Bottom); |
|
1793 |
|
1794 if (p1 & p2) |
|
1795 // completely inside |
|
1796 return false; |
|
1797 |
|
1798 if (p1 | p2) { |
|
1799 qreal dx = x2 - x1; |
|
1800 qreal dy = y2 - y1; |
|
1801 |
|
1802 // clip x coordinates |
|
1803 if (x1 < left) { |
|
1804 y1 += dy/dx * (left - x1); |
|
1805 x1 = left; |
|
1806 } else if (x1 > right) { |
|
1807 y1 -= dy/dx * (x1 - right); |
|
1808 x1 = right; |
|
1809 } |
|
1810 if (x2 < left) { |
|
1811 y2 += dy/dx * (left - x2); |
|
1812 x2 = left; |
|
1813 } else if (x2 > right) { |
|
1814 y2 -= dy/dx * (x2 - right); |
|
1815 x2 = right; |
|
1816 } |
|
1817 |
|
1818 p1 = ((y1 < top) << Top) |
|
1819 | ((y1 > bottom) << Bottom); |
|
1820 p2 = ((y2 < top) << Top) |
|
1821 | ((y2 > bottom) << Bottom); |
|
1822 |
|
1823 if (p1 & p2) |
|
1824 return false; |
|
1825 |
|
1826 // clip y coordinates |
|
1827 if (y1 < top) { |
|
1828 x1 += dx/dy * (top - y1); |
|
1829 y1 = top; |
|
1830 } else if (y1 > bottom) { |
|
1831 x1 -= dx/dy * (y1 - bottom); |
|
1832 y1 = bottom; |
|
1833 } |
|
1834 if (y2 < top) { |
|
1835 x2 += dx/dy * (top - y2); |
|
1836 y2 = top; |
|
1837 } else if (y2 > bottom) { |
|
1838 x2 -= dx/dy * (y2 - bottom); |
|
1839 y2 = bottom; |
|
1840 } |
|
1841 |
|
1842 p1 = ((x1 < left) << Left) |
|
1843 | ((x1 > right) << Right); |
|
1844 p2 = ((x2 < left) << Left) |
|
1845 | ((x2 > right) << Right); |
|
1846 |
|
1847 if (p1 & p2) |
|
1848 return false; |
|
1849 |
|
1850 return true; |
|
1851 } |
|
1852 return false; |
|
1853 } |
|
1854 |
|
1855 static bool qt_isect_curve_horizontal(const QBezier &bezier, qreal y, qreal x1, qreal x2) |
|
1856 { |
|
1857 QRectF bounds = bezier.bounds(); |
|
1858 |
|
1859 if (y >= bounds.top() && y < bounds.bottom() |
|
1860 && bounds.right() >= x1 && bounds.left() < x2) { |
|
1861 const qreal lower_bound = qreal(.01); |
|
1862 if (bounds.width() < lower_bound && bounds.height() < lower_bound) |
|
1863 return true; |
|
1864 |
|
1865 QBezier first_half, second_half; |
|
1866 bezier.split(&first_half, &second_half); |
|
1867 if (qt_isect_curve_horizontal(first_half, y, x1, x2) |
|
1868 || qt_isect_curve_horizontal(second_half, y, x1, x2)) |
|
1869 return true; |
|
1870 } |
|
1871 return false; |
|
1872 } |
|
1873 |
|
1874 static bool qt_isect_curve_vertical(const QBezier &bezier, qreal x, qreal y1, qreal y2) |
|
1875 { |
|
1876 QRectF bounds = bezier.bounds(); |
|
1877 |
|
1878 if (x >= bounds.left() && x < bounds.right() |
|
1879 && bounds.bottom() >= y1 && bounds.top() < y2) { |
|
1880 const qreal lower_bound = qreal(.01); |
|
1881 if (bounds.width() < lower_bound && bounds.height() < lower_bound) |
|
1882 return true; |
|
1883 |
|
1884 QBezier first_half, second_half; |
|
1885 bezier.split(&first_half, &second_half); |
|
1886 if (qt_isect_curve_vertical(first_half, x, y1, y2) |
|
1887 || qt_isect_curve_vertical(second_half, x, y1, y2)) |
|
1888 return true; |
|
1889 } |
|
1890 return false; |
|
1891 } |
|
1892 |
|
1893 /* |
|
1894 Returns true if any lines or curves cross the four edges in of rect |
|
1895 */ |
|
1896 static bool qt_painterpath_check_crossing(const QPainterPath *path, const QRectF &rect) |
|
1897 { |
|
1898 QPointF last_pt; |
|
1899 QPointF last_start; |
|
1900 for (int i=0; i<path->elementCount(); ++i) { |
|
1901 const QPainterPath::Element &e = path->elementAt(i); |
|
1902 |
|
1903 switch (e.type) { |
|
1904 |
|
1905 case QPainterPath::MoveToElement: |
|
1906 if (i > 0 |
|
1907 && qFuzzyCompare(last_pt.x(), last_start.y()) |
|
1908 && qFuzzyCompare(last_pt.y(), last_start.y()) |
|
1909 && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(), |
|
1910 last_start.x(), last_start.y(), rect)) |
|
1911 return true; |
|
1912 last_start = last_pt = e; |
|
1913 break; |
|
1914 |
|
1915 case QPainterPath::LineToElement: |
|
1916 if (qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(), e.x, e.y, rect)) |
|
1917 return true; |
|
1918 last_pt = e; |
|
1919 break; |
|
1920 |
|
1921 case QPainterPath::CurveToElement: |
|
1922 { |
|
1923 QPointF cp2 = path->elementAt(++i); |
|
1924 QPointF ep = path->elementAt(++i); |
|
1925 QBezier bezier = QBezier::fromPoints(last_pt, e, cp2, ep); |
|
1926 if (qt_isect_curve_horizontal(bezier, rect.top(), rect.left(), rect.right()) |
|
1927 || qt_isect_curve_horizontal(bezier, rect.bottom(), rect.left(), rect.right()) |
|
1928 || qt_isect_curve_vertical(bezier, rect.left(), rect.top(), rect.bottom()) |
|
1929 || qt_isect_curve_vertical(bezier, rect.right(), rect.top(), rect.bottom())) |
|
1930 return true; |
|
1931 last_pt = ep; |
|
1932 } |
|
1933 break; |
|
1934 |
|
1935 default: |
|
1936 break; |
|
1937 } |
|
1938 } |
|
1939 |
|
1940 // implicitly close last subpath |
|
1941 if (last_pt != last_start |
|
1942 && qt_painterpath_isect_line_rect(last_pt.x(), last_pt.y(), |
|
1943 last_start.x(), last_start.y(), rect)) |
|
1944 return true; |
|
1945 |
|
1946 return false; |
|
1947 } |
|
1948 |
|
1949 /*! |
|
1950 \fn bool QPainterPath::intersects(const QRectF &rectangle) const |
|
1951 |
|
1952 Returns true if any point in the given \a rectangle intersects the |
|
1953 path; otherwise returns false. |
|
1954 |
|
1955 There is an intersection if any of the lines making up the |
|
1956 rectangle crosses a part of the path or if any part of the |
|
1957 rectangle overlaps with any area enclosed by the path. This |
|
1958 function respects the current fillRule to determine what is |
|
1959 considered inside the path. |
|
1960 |
|
1961 \sa contains() |
|
1962 */ |
|
1963 bool QPainterPath::intersects(const QRectF &rect) const |
|
1964 { |
|
1965 if (elementCount() == 1 && rect.contains(elementAt(0))) |
|
1966 return true; |
|
1967 |
|
1968 if (isEmpty()) |
|
1969 return false; |
|
1970 |
|
1971 QRectF cp = controlPointRect(); |
|
1972 QRectF rn = rect.normalized(); |
|
1973 |
|
1974 // QRectF::intersects returns false if one of the rects is a null rect |
|
1975 // which would happen for a painter path consisting of a vertical or |
|
1976 // horizontal line |
|
1977 if (qMax(rn.left(), cp.left()) > qMin(rn.right(), cp.right()) |
|
1978 || qMax(rn.top(), cp.top()) > qMin(rn.bottom(), cp.bottom())) |
|
1979 return false; |
|
1980 |
|
1981 // If any path element cross the rect its bound to be an intersection |
|
1982 if (qt_painterpath_check_crossing(this, rect)) |
|
1983 return true; |
|
1984 |
|
1985 if (contains(rect.center())) |
|
1986 return true; |
|
1987 |
|
1988 Q_D(QPainterPath); |
|
1989 |
|
1990 // Check if the rectangle surounds any subpath... |
|
1991 for (int i=0; i<d->elements.size(); ++i) { |
|
1992 const Element &e = d->elements.at(i); |
|
1993 if (e.type == QPainterPath::MoveToElement && rect.contains(e)) |
|
1994 return true; |
|
1995 } |
|
1996 |
|
1997 return false; |
|
1998 } |
|
1999 |
|
2000 /*! |
|
2001 Translates all elements in the path by (\a{dx}, \a{dy}). |
|
2002 |
|
2003 \since 4.6 |
|
2004 \sa translated() |
|
2005 */ |
|
2006 void QPainterPath::translate(qreal dx, qreal dy) |
|
2007 { |
|
2008 if (!d_ptr || (dx == 0 && dy == 0)) |
|
2009 return; |
|
2010 |
|
2011 int elementsLeft = d_ptr->elements.size(); |
|
2012 if (elementsLeft <= 0) |
|
2013 return; |
|
2014 |
|
2015 detach(); |
|
2016 QPainterPath::Element *element = d_func()->elements.data(); |
|
2017 Q_ASSERT(element); |
|
2018 while (elementsLeft--) { |
|
2019 element->x += dx; |
|
2020 element->y += dy; |
|
2021 ++element; |
|
2022 } |
|
2023 } |
|
2024 |
|
2025 /*! |
|
2026 \fn void QPainterPath::translate(const QPointF &offset) |
|
2027 \overload |
|
2028 \since 4.6 |
|
2029 |
|
2030 Translates all elements in the path by the given \a offset. |
|
2031 |
|
2032 \sa translated() |
|
2033 */ |
|
2034 |
|
2035 /*! |
|
2036 Returns a copy of the path that is translated by (\a{dx}, \a{dy}). |
|
2037 |
|
2038 \since 4.6 |
|
2039 \sa translate() |
|
2040 */ |
|
2041 QPainterPath QPainterPath::translated(qreal dx, qreal dy) const |
|
2042 { |
|
2043 QPainterPath copy(*this); |
|
2044 copy.translate(dx, dy); |
|
2045 return copy; |
|
2046 } |
|
2047 |
|
2048 /*! |
|
2049 \fn QPainterPath QPainterPath::translated(const QPointF &offset) const; |
|
2050 \overload |
|
2051 \since 4.6 |
|
2052 |
|
2053 Returns a copy of the path that is translated by the given \a offset. |
|
2054 |
|
2055 \sa translate() |
|
2056 */ |
|
2057 |
|
2058 /*! |
|
2059 \fn bool QPainterPath::contains(const QRectF &rectangle) const |
|
2060 |
|
2061 Returns true if the given \a rectangle is inside the path, |
|
2062 otherwise returns false. |
|
2063 */ |
|
2064 bool QPainterPath::contains(const QRectF &rect) const |
|
2065 { |
|
2066 Q_D(QPainterPath); |
|
2067 |
|
2068 // the path is empty or the control point rect doesn't completely |
|
2069 // cover the rectangle we abort stratight away. |
|
2070 if (isEmpty() || !controlPointRect().contains(rect)) |
|
2071 return false; |
|
2072 |
|
2073 // if there are intersections, chances are that the rect is not |
|
2074 // contained, except if we have winding rule, in which case it |
|
2075 // still might. |
|
2076 if (qt_painterpath_check_crossing(this, rect)) { |
|
2077 if (fillRule() == Qt::OddEvenFill) { |
|
2078 return false; |
|
2079 } else { |
|
2080 // Do some wague sampling in the winding case. This is not |
|
2081 // precise but it should mostly be good enough. |
|
2082 if (!contains(rect.topLeft()) || |
|
2083 !contains(rect.topRight()) || |
|
2084 !contains(rect.bottomRight()) || |
|
2085 !contains(rect.bottomLeft())) |
|
2086 return false; |
|
2087 } |
|
2088 } |
|
2089 |
|
2090 // If there exists a point inside that is not part of the path its |
|
2091 // because: rectangle lies completely outside path or a subpath |
|
2092 // excludes parts of the rectangle. Both cases mean that the rect |
|
2093 // is not contained |
|
2094 if (!contains(rect.center())) |
|
2095 return false; |
|
2096 |
|
2097 // If there are any subpaths inside this rectangle we need to |
|
2098 // check if they are still contained as a result of the fill |
|
2099 // rule. This can only be the case for WindingFill though. For |
|
2100 // OddEvenFill the rect will never be contained if it surrounds a |
|
2101 // subpath. (the case where two subpaths are completely identical |
|
2102 // can be argued but we choose to neglect it). |
|
2103 for (int i=0; i<d->elements.size(); ++i) { |
|
2104 const Element &e = d->elements.at(i); |
|
2105 if (e.type == QPainterPath::MoveToElement && rect.contains(e)) { |
|
2106 if (fillRule() == Qt::OddEvenFill) |
|
2107 return false; |
|
2108 |
|
2109 bool stop = false; |
|
2110 for (; !stop && i<d->elements.size(); ++i) { |
|
2111 const Element &el = d->elements.at(i); |
|
2112 switch (el.type) { |
|
2113 case MoveToElement: |
|
2114 stop = true; |
|
2115 break; |
|
2116 case LineToElement: |
|
2117 if (!contains(el)) |
|
2118 return false; |
|
2119 break; |
|
2120 case CurveToElement: |
|
2121 if (!contains(d->elements.at(i+2))) |
|
2122 return false; |
|
2123 i += 2; |
|
2124 break; |
|
2125 default: |
|
2126 break; |
|
2127 } |
|
2128 } |
|
2129 |
|
2130 // compensate for the last ++i in the inner for |
|
2131 --i; |
|
2132 } |
|
2133 } |
|
2134 |
|
2135 return true; |
|
2136 } |
|
2137 |
|
2138 static inline bool epsilonCompare(const QPointF &a, const QPointF &b, const QSizeF &epsilon) |
|
2139 { |
|
2140 return qAbs(a.x() - b.x()) <= epsilon.width() |
|
2141 && qAbs(a.y() - b.y()) <= epsilon.height(); |
|
2142 } |
|
2143 |
|
2144 /*! |
|
2145 Returns true if this painterpath is equal to the given \a path. |
|
2146 |
|
2147 Note that comparing paths may involve a per element comparison |
|
2148 which can be slow for complex paths. |
|
2149 |
|
2150 \sa operator!=() |
|
2151 */ |
|
2152 |
|
2153 bool QPainterPath::operator==(const QPainterPath &path) const |
|
2154 { |
|
2155 QPainterPathData *d = reinterpret_cast<QPainterPathData *>(d_func()); |
|
2156 if (path.d_func() == d) |
|
2157 return true; |
|
2158 else if (!d || !path.d_func()) |
|
2159 return false; |
|
2160 else if (d->fillRule != path.d_func()->fillRule) |
|
2161 return false; |
|
2162 else if (d->elements.size() != path.d_func()->elements.size()) |
|
2163 return false; |
|
2164 |
|
2165 const qreal qt_epsilon = sizeof(qreal) == sizeof(double) ? 1e-12 : qreal(1e-5); |
|
2166 |
|
2167 QSizeF epsilon = boundingRect().size(); |
|
2168 epsilon.rwidth() *= qt_epsilon; |
|
2169 epsilon.rheight() *= qt_epsilon; |
|
2170 |
|
2171 for (int i = 0; i < d->elements.size(); ++i) |
|
2172 if (d->elements.at(i).type != path.d_func()->elements.at(i).type |
|
2173 || !epsilonCompare(d->elements.at(i), path.d_func()->elements.at(i), epsilon)) |
|
2174 return false; |
|
2175 |
|
2176 return true; |
|
2177 } |
|
2178 |
|
2179 /*! |
|
2180 Returns true if this painter path differs from the given \a path. |
|
2181 |
|
2182 Note that comparing paths may involve a per element comparison |
|
2183 which can be slow for complex paths. |
|
2184 |
|
2185 \sa operator==() |
|
2186 */ |
|
2187 |
|
2188 bool QPainterPath::operator!=(const QPainterPath &path) const |
|
2189 { |
|
2190 return !(*this==path); |
|
2191 } |
|
2192 |
|
2193 /*! |
|
2194 \since 4.5 |
|
2195 |
|
2196 Returns the intersection of this path and the \a other path. |
|
2197 |
|
2198 \sa intersected(), operator&=(), united(), operator|() |
|
2199 */ |
|
2200 QPainterPath QPainterPath::operator&(const QPainterPath &other) const |
|
2201 { |
|
2202 return intersected(other); |
|
2203 } |
|
2204 |
|
2205 /*! |
|
2206 \since 4.5 |
|
2207 |
|
2208 Returns the union of this path and the \a other path. |
|
2209 |
|
2210 \sa united(), operator|=(), intersected(), operator&() |
|
2211 */ |
|
2212 QPainterPath QPainterPath::operator|(const QPainterPath &other) const |
|
2213 { |
|
2214 return united(other); |
|
2215 } |
|
2216 |
|
2217 /*! |
|
2218 \since 4.5 |
|
2219 |
|
2220 Returns the union of this path and the \a other path. This function is equivalent |
|
2221 to operator|(). |
|
2222 |
|
2223 \sa united(), operator+=(), operator-() |
|
2224 */ |
|
2225 QPainterPath QPainterPath::operator+(const QPainterPath &other) const |
|
2226 { |
|
2227 return united(other); |
|
2228 } |
|
2229 |
|
2230 /*! |
|
2231 \since 4.5 |
|
2232 |
|
2233 Subtracts the \a other path from a copy of this path, and returns the copy. |
|
2234 |
|
2235 \sa subtracted(), operator-=(), operator+() |
|
2236 */ |
|
2237 QPainterPath QPainterPath::operator-(const QPainterPath &other) const |
|
2238 { |
|
2239 return subtracted(other); |
|
2240 } |
|
2241 |
|
2242 /*! |
|
2243 \since 4.5 |
|
2244 |
|
2245 Intersects this path with \a other and returns a reference to this path. |
|
2246 |
|
2247 \sa intersected(), operator&(), operator|=() |
|
2248 */ |
|
2249 QPainterPath &QPainterPath::operator&=(const QPainterPath &other) |
|
2250 { |
|
2251 return *this = (*this & other); |
|
2252 } |
|
2253 |
|
2254 /*! |
|
2255 \since 4.5 |
|
2256 |
|
2257 Unites this path with \a other and returns a reference to this path. |
|
2258 |
|
2259 \sa united(), operator|(), operator&=() |
|
2260 */ |
|
2261 QPainterPath &QPainterPath::operator|=(const QPainterPath &other) |
|
2262 { |
|
2263 return *this = (*this | other); |
|
2264 } |
|
2265 |
|
2266 /*! |
|
2267 \since 4.5 |
|
2268 |
|
2269 Unites this path with \a other, and returns a reference to this path. This |
|
2270 is equivalent to operator|=(). |
|
2271 |
|
2272 \sa united(), operator+(), operator-=() |
|
2273 */ |
|
2274 QPainterPath &QPainterPath::operator+=(const QPainterPath &other) |
|
2275 { |
|
2276 return *this = (*this + other); |
|
2277 } |
|
2278 |
|
2279 /*! |
|
2280 \since 4.5 |
|
2281 |
|
2282 Subtracts \a other from this path, and returns a reference to this |
|
2283 path. |
|
2284 |
|
2285 \sa subtracted(), operator-(), operator+=() |
|
2286 */ |
|
2287 QPainterPath &QPainterPath::operator-=(const QPainterPath &other) |
|
2288 { |
|
2289 return *this = (*this - other); |
|
2290 } |
|
2291 |
|
2292 #ifndef QT_NO_DATASTREAM |
|
2293 /*! |
|
2294 \fn QDataStream &operator<<(QDataStream &stream, const QPainterPath &path) |
|
2295 \relates QPainterPath |
|
2296 |
|
2297 Writes the given painter \a path to the given \a stream, and |
|
2298 returns a reference to the \a stream. |
|
2299 |
|
2300 \sa {Format of the QDataStream Operators} |
|
2301 */ |
|
2302 QDataStream &operator<<(QDataStream &s, const QPainterPath &p) |
|
2303 { |
|
2304 if (p.isEmpty()) { |
|
2305 s << 0; |
|
2306 return s; |
|
2307 } |
|
2308 |
|
2309 s << p.elementCount(); |
|
2310 for (int i=0; i < p.d_func()->elements.size(); ++i) { |
|
2311 const QPainterPath::Element &e = p.d_func()->elements.at(i); |
|
2312 s << int(e.type); |
|
2313 s << double(e.x) << double(e.y); |
|
2314 } |
|
2315 s << p.d_func()->cStart; |
|
2316 s << int(p.d_func()->fillRule); |
|
2317 return s; |
|
2318 } |
|
2319 |
|
2320 /*! |
|
2321 \fn QDataStream &operator>>(QDataStream &stream, QPainterPath &path) |
|
2322 \relates QPainterPath |
|
2323 |
|
2324 Reads a painter path from the given \a stream into the specified \a path, |
|
2325 and returns a reference to the \a stream. |
|
2326 |
|
2327 \sa {Format of the QDataStream Operators} |
|
2328 */ |
|
2329 QDataStream &operator>>(QDataStream &s, QPainterPath &p) |
|
2330 { |
|
2331 int size; |
|
2332 s >> size; |
|
2333 |
|
2334 if (size == 0) |
|
2335 return s; |
|
2336 |
|
2337 p.ensureData(); // in case if p.d_func() == 0 |
|
2338 if (p.d_func()->elements.size() == 1) { |
|
2339 Q_ASSERT(p.d_func()->elements.at(0).type == QPainterPath::MoveToElement); |
|
2340 p.d_func()->elements.clear(); |
|
2341 } |
|
2342 p.d_func()->elements.reserve(p.d_func()->elements.size() + size); |
|
2343 for (int i=0; i<size; ++i) { |
|
2344 int type; |
|
2345 double x, y; |
|
2346 s >> type; |
|
2347 s >> x; |
|
2348 s >> y; |
|
2349 Q_ASSERT(type >= 0 && type <= 3); |
|
2350 #ifndef QT_NO_DEBUG |
|
2351 if (qt_is_nan(x) || qt_is_nan(y)) |
|
2352 qWarning("QDataStream::operator>>: Adding a NaN element to path, results are undefined"); |
|
2353 #endif |
|
2354 QPainterPath::Element elm = { x, y, QPainterPath::ElementType(type) }; |
|
2355 p.d_func()->elements.append(elm); |
|
2356 } |
|
2357 s >> p.d_func()->cStart; |
|
2358 int fillRule; |
|
2359 s >> fillRule; |
|
2360 Q_ASSERT(fillRule == Qt::OddEvenFill || Qt::WindingFill); |
|
2361 p.d_func()->fillRule = Qt::FillRule(fillRule); |
|
2362 p.d_func()->dirtyBounds = true; |
|
2363 p.d_func()->dirtyControlBounds = true; |
|
2364 return s; |
|
2365 } |
|
2366 #endif // QT_NO_DATASTREAM |
|
2367 |
|
2368 |
|
2369 /******************************************************************************* |
|
2370 * class QPainterPathStroker |
|
2371 */ |
|
2372 |
|
2373 void qt_path_stroke_move_to(qfixed x, qfixed y, void *data) |
|
2374 { |
|
2375 ((QPainterPath *) data)->moveTo(qt_fixed_to_real(x), qt_fixed_to_real(y)); |
|
2376 } |
|
2377 |
|
2378 void qt_path_stroke_line_to(qfixed x, qfixed y, void *data) |
|
2379 { |
|
2380 ((QPainterPath *) data)->lineTo(qt_fixed_to_real(x), qt_fixed_to_real(y)); |
|
2381 } |
|
2382 |
|
2383 void qt_path_stroke_cubic_to(qfixed c1x, qfixed c1y, |
|
2384 qfixed c2x, qfixed c2y, |
|
2385 qfixed ex, qfixed ey, |
|
2386 void *data) |
|
2387 { |
|
2388 ((QPainterPath *) data)->cubicTo(qt_fixed_to_real(c1x), qt_fixed_to_real(c1y), |
|
2389 qt_fixed_to_real(c2x), qt_fixed_to_real(c2y), |
|
2390 qt_fixed_to_real(ex), qt_fixed_to_real(ey)); |
|
2391 } |
|
2392 |
|
2393 /*! |
|
2394 \since 4.1 |
|
2395 \class QPainterPathStroker |
|
2396 \ingroup painting |
|
2397 |
|
2398 \brief The QPainterPathStroker class is used to generate fillable |
|
2399 outlines for a given painter path. |
|
2400 |
|
2401 By calling the createStroke() function, passing a given |
|
2402 QPainterPath as argument, a new painter path representing the |
|
2403 outline of the given path is created. The newly created painter |
|
2404 path can then be filled to draw the original painter path's |
|
2405 outline. |
|
2406 |
|
2407 You can control the various design aspects (width, cap styles, |
|
2408 join styles and dash pattern) of the outlining using the following |
|
2409 functions: |
|
2410 |
|
2411 \list |
|
2412 \o setWidth() |
|
2413 \o setCapStyle() |
|
2414 \o setJoinStyle() |
|
2415 \o setDashPattern() |
|
2416 \endlist |
|
2417 |
|
2418 The setDashPattern() function accepts both a Qt::PenStyle object |
|
2419 and a vector representation of the pattern as argument. |
|
2420 |
|
2421 In addition you can specify a curve's threshold, controlling the |
|
2422 granularity with which a curve is drawn, using the |
|
2423 setCurveThreshold() function. The default threshold is a well |
|
2424 adjusted value (0.25), and normally you should not need to modify |
|
2425 it. However, you can make the curve's appearance smoother by |
|
2426 decreasing its value. |
|
2427 |
|
2428 You can also control the miter limit for the generated outline |
|
2429 using the setMiterLimit() function. The miter limit describes how |
|
2430 far from each join the miter join can extend. The limit is |
|
2431 specified in the units of width so the pixelwise miter limit will |
|
2432 be \c {miterlimit * width}. This value is only used if the join |
|
2433 style is Qt::MiterJoin. |
|
2434 |
|
2435 The painter path generated by the createStroke() function should |
|
2436 only be used for outlining the given painter path. Otherwise it |
|
2437 may cause unexpected behavior. Generated outlines also require the |
|
2438 Qt::WindingFill rule which is set by default. |
|
2439 |
|
2440 \sa QPen, QBrush |
|
2441 */ |
|
2442 |
|
2443 QPainterPathStrokerPrivate::QPainterPathStrokerPrivate() |
|
2444 : dashOffset(0) |
|
2445 { |
|
2446 stroker.setMoveToHook(qt_path_stroke_move_to); |
|
2447 stroker.setLineToHook(qt_path_stroke_line_to); |
|
2448 stroker.setCubicToHook(qt_path_stroke_cubic_to); |
|
2449 } |
|
2450 |
|
2451 /*! |
|
2452 Creates a new stroker. |
|
2453 */ |
|
2454 QPainterPathStroker::QPainterPathStroker() |
|
2455 : d_ptr(new QPainterPathStrokerPrivate) |
|
2456 { |
|
2457 } |
|
2458 |
|
2459 /*! |
|
2460 Destroys the stroker. |
|
2461 */ |
|
2462 QPainterPathStroker::~QPainterPathStroker() |
|
2463 { |
|
2464 } |
|
2465 |
|
2466 |
|
2467 /*! |
|
2468 Generates a new path that is a fillable area representing the |
|
2469 outline of the given \a path. |
|
2470 |
|
2471 The various design aspects of the outline are based on the |
|
2472 stroker's properties: width(), capStyle(), joinStyle(), |
|
2473 dashPattern(), curveThreshold() and miterLimit(). |
|
2474 |
|
2475 The generated path should only be used for outlining the given |
|
2476 painter path. Otherwise it may cause unexpected |
|
2477 behavior. Generated outlines also require the Qt::WindingFill rule |
|
2478 which is set by default. |
|
2479 */ |
|
2480 QPainterPath QPainterPathStroker::createStroke(const QPainterPath &path) const |
|
2481 { |
|
2482 QPainterPathStrokerPrivate *d = const_cast<QPainterPathStrokerPrivate *>(d_func()); |
|
2483 QPainterPath stroke; |
|
2484 if (path.isEmpty()) |
|
2485 return path; |
|
2486 if (d->dashPattern.isEmpty()) { |
|
2487 d->stroker.strokePath(path, &stroke, QTransform()); |
|
2488 } else { |
|
2489 QDashStroker dashStroker(&d->stroker); |
|
2490 dashStroker.setDashPattern(d->dashPattern); |
|
2491 dashStroker.setDashOffset(d->dashOffset); |
|
2492 dashStroker.setClipRect(d->stroker.clipRect()); |
|
2493 dashStroker.strokePath(path, &stroke, QTransform()); |
|
2494 } |
|
2495 stroke.setFillRule(Qt::WindingFill); |
|
2496 return stroke; |
|
2497 } |
|
2498 |
|
2499 /*! |
|
2500 Sets the width of the generated outline painter path to \a width. |
|
2501 |
|
2502 The generated outlines will extend approximately 50% of \a width |
|
2503 to each side of the given input path's original outline. |
|
2504 */ |
|
2505 void QPainterPathStroker::setWidth(qreal width) |
|
2506 { |
|
2507 Q_D(QPainterPathStroker); |
|
2508 if (width <= 0) |
|
2509 width = 1; |
|
2510 d->stroker.setStrokeWidth(qt_real_to_fixed(width)); |
|
2511 } |
|
2512 |
|
2513 /*! |
|
2514 Returns the width of the generated outlines. |
|
2515 */ |
|
2516 qreal QPainterPathStroker::width() const |
|
2517 { |
|
2518 return qt_fixed_to_real(d_func()->stroker.strokeWidth()); |
|
2519 } |
|
2520 |
|
2521 |
|
2522 /*! |
|
2523 Sets the cap style of the generated outlines to \a style. If a |
|
2524 dash pattern is set, each segment of the pattern is subject to the |
|
2525 cap \a style. |
|
2526 */ |
|
2527 void QPainterPathStroker::setCapStyle(Qt::PenCapStyle style) |
|
2528 { |
|
2529 d_func()->stroker.setCapStyle(style); |
|
2530 } |
|
2531 |
|
2532 |
|
2533 /*! |
|
2534 Returns the cap style of the generated outlines. |
|
2535 */ |
|
2536 Qt::PenCapStyle QPainterPathStroker::capStyle() const |
|
2537 { |
|
2538 return d_func()->stroker.capStyle(); |
|
2539 } |
|
2540 |
|
2541 /*! |
|
2542 Sets the join style of the generated outlines to \a style. |
|
2543 */ |
|
2544 void QPainterPathStroker::setJoinStyle(Qt::PenJoinStyle style) |
|
2545 { |
|
2546 d_func()->stroker.setJoinStyle(style); |
|
2547 } |
|
2548 |
|
2549 /*! |
|
2550 Returns the join style of the generated outlines. |
|
2551 */ |
|
2552 Qt::PenJoinStyle QPainterPathStroker::joinStyle() const |
|
2553 { |
|
2554 return d_func()->stroker.joinStyle(); |
|
2555 } |
|
2556 |
|
2557 /*! |
|
2558 Sets the miter limit of the generated outlines to \a limit. |
|
2559 |
|
2560 The miter limit describes how far from each join the miter join |
|
2561 can extend. The limit is specified in units of the currently set |
|
2562 width. So the pixelwise miter limit will be \c { miterlimit * |
|
2563 width}. |
|
2564 |
|
2565 This value is only used if the join style is Qt::MiterJoin. |
|
2566 */ |
|
2567 void QPainterPathStroker::setMiterLimit(qreal limit) |
|
2568 { |
|
2569 d_func()->stroker.setMiterLimit(qt_real_to_fixed(limit)); |
|
2570 } |
|
2571 |
|
2572 /*! |
|
2573 Returns the miter limit for the generated outlines. |
|
2574 */ |
|
2575 qreal QPainterPathStroker::miterLimit() const |
|
2576 { |
|
2577 return qt_fixed_to_real(d_func()->stroker.miterLimit()); |
|
2578 } |
|
2579 |
|
2580 |
|
2581 /*! |
|
2582 Specifies the curve flattening \a threshold, controlling the |
|
2583 granularity with which the generated outlines' curve is drawn. |
|
2584 |
|
2585 The default threshold is a well adjusted value (0.25), and |
|
2586 normally you should not need to modify it. However, you can make |
|
2587 the curve's appearance smoother by decreasing its value. |
|
2588 */ |
|
2589 void QPainterPathStroker::setCurveThreshold(qreal threshold) |
|
2590 { |
|
2591 d_func()->stroker.setCurveThreshold(qt_real_to_fixed(threshold)); |
|
2592 } |
|
2593 |
|
2594 /*! |
|
2595 Returns the curve flattening threshold for the generated |
|
2596 outlines. |
|
2597 */ |
|
2598 qreal QPainterPathStroker::curveThreshold() const |
|
2599 { |
|
2600 return qt_fixed_to_real(d_func()->stroker.curveThreshold()); |
|
2601 } |
|
2602 |
|
2603 /*! |
|
2604 Sets the dash pattern for the generated outlines to \a style. |
|
2605 */ |
|
2606 void QPainterPathStroker::setDashPattern(Qt::PenStyle style) |
|
2607 { |
|
2608 d_func()->dashPattern = QDashStroker::patternForStyle(style); |
|
2609 } |
|
2610 |
|
2611 /*! |
|
2612 \overload |
|
2613 |
|
2614 Sets the dash pattern for the generated outlines to \a |
|
2615 dashPattern. This function makes it possible to specify custom |
|
2616 dash patterns. |
|
2617 |
|
2618 Each element in the vector contains the lengths of the dashes and spaces |
|
2619 in the stroke, beginning with the first dash in the first element, the |
|
2620 first space in the second element, and alternating between dashes and |
|
2621 spaces for each following pair of elements. |
|
2622 |
|
2623 The vector can contain an odd number of elements, in which case the last |
|
2624 element will be extended by the length of the first element when the |
|
2625 pattern repeats. |
|
2626 */ |
|
2627 void QPainterPathStroker::setDashPattern(const QVector<qreal> &dashPattern) |
|
2628 { |
|
2629 d_func()->dashPattern.clear(); |
|
2630 for (int i=0; i<dashPattern.size(); ++i) |
|
2631 d_func()->dashPattern << qt_real_to_fixed(dashPattern.at(i)); |
|
2632 } |
|
2633 |
|
2634 /*! |
|
2635 Returns the dash pattern for the generated outlines. |
|
2636 */ |
|
2637 QVector<qreal> QPainterPathStroker::dashPattern() const |
|
2638 { |
|
2639 return d_func()->dashPattern; |
|
2640 } |
|
2641 |
|
2642 /*! |
|
2643 Returns the dash offset for the generated outlines. |
|
2644 */ |
|
2645 qreal QPainterPathStroker::dashOffset() const |
|
2646 { |
|
2647 return d_func()->dashOffset; |
|
2648 } |
|
2649 |
|
2650 /*! |
|
2651 Sets the dash offset for the generated outlines to \a offset. |
|
2652 |
|
2653 See the documentation for QPen::setDashOffset() for a description of the |
|
2654 dash offset. |
|
2655 */ |
|
2656 void QPainterPathStroker::setDashOffset(qreal offset) |
|
2657 { |
|
2658 d_func()->dashOffset = offset; |
|
2659 } |
|
2660 |
|
2661 /*! |
|
2662 Converts the path into a polygon using the QTransform |
|
2663 \a matrix, and returns the polygon. |
|
2664 |
|
2665 The polygon is created by first converting all subpaths to |
|
2666 polygons, then using a rewinding technique to make sure that |
|
2667 overlapping subpaths can be filled using the correct fill rule. |
|
2668 |
|
2669 Note that rewinding inserts addition lines in the polygon so |
|
2670 the outline of the fill polygon does not match the outline of |
|
2671 the path. |
|
2672 |
|
2673 \sa toSubpathPolygons(), toFillPolygons(), |
|
2674 {QPainterPath#QPainterPath Conversion}{QPainterPath Conversion} |
|
2675 */ |
|
2676 QPolygonF QPainterPath::toFillPolygon(const QTransform &matrix) const |
|
2677 { |
|
2678 |
|
2679 QList<QPolygonF> flats = toSubpathPolygons(matrix); |
|
2680 QPolygonF polygon; |
|
2681 if (flats.isEmpty()) |
|
2682 return polygon; |
|
2683 QPointF first = flats.first().first(); |
|
2684 for (int i=0; i<flats.size(); ++i) { |
|
2685 polygon += flats.at(i); |
|
2686 if (!flats.at(i).isClosed()) |
|
2687 polygon += flats.at(i).first(); |
|
2688 if (i > 0) |
|
2689 polygon += first; |
|
2690 } |
|
2691 return polygon; |
|
2692 } |
|
2693 |
|
2694 /*! |
|
2695 \overload |
|
2696 */ |
|
2697 QPolygonF QPainterPath::toFillPolygon(const QMatrix &matrix) const |
|
2698 { |
|
2699 return toFillPolygon(QTransform(matrix)); |
|
2700 } |
|
2701 |
|
2702 |
|
2703 //derivative of the equation |
|
2704 static inline qreal slopeAt(qreal t, qreal a, qreal b, qreal c, qreal d) |
|
2705 { |
|
2706 return 3*t*t*(d - 3*c + 3*b - a) + 6*t*(c - 2*b + a) + 3*(b - a); |
|
2707 } |
|
2708 |
|
2709 /*! |
|
2710 Returns the length of the current path. |
|
2711 */ |
|
2712 qreal QPainterPath::length() const |
|
2713 { |
|
2714 Q_D(QPainterPath); |
|
2715 if (isEmpty()) |
|
2716 return 0; |
|
2717 |
|
2718 qreal len = 0; |
|
2719 for (int i=1; i<d->elements.size(); ++i) { |
|
2720 const Element &e = d->elements.at(i); |
|
2721 |
|
2722 switch (e.type) { |
|
2723 case MoveToElement: |
|
2724 break; |
|
2725 case LineToElement: |
|
2726 { |
|
2727 len += QLineF(d->elements.at(i-1), e).length(); |
|
2728 break; |
|
2729 } |
|
2730 case CurveToElement: |
|
2731 { |
|
2732 QBezier b = QBezier::fromPoints(d->elements.at(i-1), |
|
2733 e, |
|
2734 d->elements.at(i+1), |
|
2735 d->elements.at(i+2)); |
|
2736 len += b.length(); |
|
2737 i += 2; |
|
2738 break; |
|
2739 } |
|
2740 default: |
|
2741 break; |
|
2742 } |
|
2743 } |
|
2744 return len; |
|
2745 } |
|
2746 |
|
2747 /*! |
|
2748 Returns percentage of the whole path at the specified length \a len. |
|
2749 |
|
2750 Note that similarly to other percent methods, the percentage measurement |
|
2751 is not linear with regards to the length, if curves are present |
|
2752 in the path. When curves are present the percentage argument is mapped |
|
2753 to the t parameter of the Bezier equations. |
|
2754 */ |
|
2755 qreal QPainterPath::percentAtLength(qreal len) const |
|
2756 { |
|
2757 Q_D(QPainterPath); |
|
2758 if (isEmpty() || len <= 0) |
|
2759 return 0; |
|
2760 |
|
2761 qreal totalLength = length(); |
|
2762 if (len > totalLength) |
|
2763 return 1; |
|
2764 |
|
2765 qreal curLen = 0; |
|
2766 for (int i=1; i<d->elements.size(); ++i) { |
|
2767 const Element &e = d->elements.at(i); |
|
2768 |
|
2769 switch (e.type) { |
|
2770 case MoveToElement: |
|
2771 break; |
|
2772 case LineToElement: |
|
2773 { |
|
2774 QLineF line(d->elements.at(i-1), e); |
|
2775 qreal llen = line.length(); |
|
2776 curLen += llen; |
|
2777 if (curLen >= len) { |
|
2778 return len/totalLength ; |
|
2779 } |
|
2780 |
|
2781 break; |
|
2782 } |
|
2783 case CurveToElement: |
|
2784 { |
|
2785 QBezier b = QBezier::fromPoints(d->elements.at(i-1), |
|
2786 e, |
|
2787 d->elements.at(i+1), |
|
2788 d->elements.at(i+2)); |
|
2789 qreal blen = b.length(); |
|
2790 qreal prevLen = curLen; |
|
2791 curLen += blen; |
|
2792 |
|
2793 if (curLen >= len) { |
|
2794 qreal res = b.tAtLength(len - prevLen); |
|
2795 return (res * blen + prevLen)/totalLength; |
|
2796 } |
|
2797 |
|
2798 i += 2; |
|
2799 break; |
|
2800 } |
|
2801 default: |
|
2802 break; |
|
2803 } |
|
2804 } |
|
2805 |
|
2806 return 0; |
|
2807 } |
|
2808 |
|
2809 static inline QBezier bezierAtT(const QPainterPath &path, qreal t, qreal *startingLength, qreal *bezierLength) |
|
2810 { |
|
2811 *startingLength = 0; |
|
2812 if (t > 1) |
|
2813 return QBezier(); |
|
2814 |
|
2815 qreal curLen = 0; |
|
2816 qreal totalLength = path.length(); |
|
2817 |
|
2818 const int lastElement = path.elementCount() - 1; |
|
2819 for (int i=0; i <= lastElement; ++i) { |
|
2820 const QPainterPath::Element &e = path.elementAt(i); |
|
2821 |
|
2822 switch (e.type) { |
|
2823 case QPainterPath::MoveToElement: |
|
2824 break; |
|
2825 case QPainterPath::LineToElement: |
|
2826 { |
|
2827 QLineF line(path.elementAt(i-1), e); |
|
2828 qreal llen = line.length(); |
|
2829 curLen += llen; |
|
2830 if (i == lastElement || curLen/totalLength >= t) { |
|
2831 *bezierLength = llen; |
|
2832 QPointF a = path.elementAt(i-1); |
|
2833 QPointF delta = e - a; |
|
2834 return QBezier::fromPoints(a, a + delta / 3, a + 2 * delta / 3, e); |
|
2835 } |
|
2836 break; |
|
2837 } |
|
2838 case QPainterPath::CurveToElement: |
|
2839 { |
|
2840 QBezier b = QBezier::fromPoints(path.elementAt(i-1), |
|
2841 e, |
|
2842 path.elementAt(i+1), |
|
2843 path.elementAt(i+2)); |
|
2844 qreal blen = b.length(); |
|
2845 curLen += blen; |
|
2846 |
|
2847 if (i + 2 == lastElement || curLen/totalLength >= t) { |
|
2848 *bezierLength = blen; |
|
2849 return b; |
|
2850 } |
|
2851 |
|
2852 i += 2; |
|
2853 break; |
|
2854 } |
|
2855 default: |
|
2856 break; |
|
2857 } |
|
2858 *startingLength = curLen; |
|
2859 } |
|
2860 return QBezier(); |
|
2861 } |
|
2862 |
|
2863 /*! |
|
2864 Returns the point at at the percentage \a t of the current path. |
|
2865 The argument \a t has to be between 0 and 1. |
|
2866 |
|
2867 Note that similarly to other percent methods, the percentage measurement |
|
2868 is not linear with regards to the length, if curves are present |
|
2869 in the path. When curves are present the percentage argument is mapped |
|
2870 to the t parameter of the Bezier equations. |
|
2871 */ |
|
2872 QPointF QPainterPath::pointAtPercent(qreal t) const |
|
2873 { |
|
2874 if (t < 0 || t > 1) { |
|
2875 qWarning("QPainterPath::pointAtPercent accepts only values between 0 and 1"); |
|
2876 return QPointF(); |
|
2877 } |
|
2878 |
|
2879 if (isEmpty()) |
|
2880 return QPointF(); |
|
2881 |
|
2882 qreal totalLength = length(); |
|
2883 qreal curLen = 0; |
|
2884 qreal bezierLen = 0; |
|
2885 QBezier b = bezierAtT(*this, t, &curLen, &bezierLen); |
|
2886 qreal realT = (totalLength * t - curLen) / bezierLen; |
|
2887 |
|
2888 return b.pointAt(qBound(qreal(0), realT, qreal(1))); |
|
2889 } |
|
2890 |
|
2891 /*! |
|
2892 Returns the angle of the path tangent at the percentage \a t. |
|
2893 The argument \a t has to be between 0 and 1. |
|
2894 |
|
2895 Positive values for the angles mean counter-clockwise while negative values |
|
2896 mean the clockwise direction. Zero degrees is at the 3 o'clock position. |
|
2897 |
|
2898 Note that similarly to the other percent methods, the percentage measurement |
|
2899 is not linear with regards to the length if curves are present |
|
2900 in the path. When curves are present the percentage argument is mapped |
|
2901 to the t parameter of the Bezier equations. |
|
2902 */ |
|
2903 qreal QPainterPath::angleAtPercent(qreal t) const |
|
2904 { |
|
2905 if (t < 0 || t > 1) { |
|
2906 qWarning("QPainterPath::angleAtPercent accepts only values between 0 and 1"); |
|
2907 return 0; |
|
2908 } |
|
2909 |
|
2910 qreal totalLength = length(); |
|
2911 qreal curLen = 0; |
|
2912 qreal bezierLen = 0; |
|
2913 QBezier bez = bezierAtT(*this, t, &curLen, &bezierLen); |
|
2914 qreal realT = (totalLength * t - curLen) / bezierLen; |
|
2915 |
|
2916 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4); |
|
2917 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4); |
|
2918 |
|
2919 return QLineF(0, 0, m1, m2).angle(); |
|
2920 } |
|
2921 |
|
2922 #if defined(Q_WS_WINCE) |
|
2923 #pragma warning( disable : 4056 4756 ) |
|
2924 #endif |
|
2925 |
|
2926 /*! |
|
2927 Returns the slope of the path at the percentage \a t. The |
|
2928 argument \a t has to be between 0 and 1. |
|
2929 |
|
2930 Note that similarly to other percent methods, the percentage measurement |
|
2931 is not linear with regards to the length, if curves are present |
|
2932 in the path. When curves are present the percentage argument is mapped |
|
2933 to the t parameter of the Bezier equations. |
|
2934 */ |
|
2935 qreal QPainterPath::slopeAtPercent(qreal t) const |
|
2936 { |
|
2937 if (t < 0 || t > 1) { |
|
2938 qWarning("QPainterPath::slopeAtPercent accepts only values between 0 and 1"); |
|
2939 return 0; |
|
2940 } |
|
2941 |
|
2942 qreal totalLength = length(); |
|
2943 qreal curLen = 0; |
|
2944 qreal bezierLen = 0; |
|
2945 QBezier bez = bezierAtT(*this, t, &curLen, &bezierLen); |
|
2946 qreal realT = (totalLength * t - curLen) / bezierLen; |
|
2947 |
|
2948 qreal m1 = slopeAt(realT, bez.x1, bez.x2, bez.x3, bez.x4); |
|
2949 qreal m2 = slopeAt(realT, bez.y1, bez.y2, bez.y3, bez.y4); |
|
2950 //tangent line |
|
2951 qreal slope = 0; |
|
2952 |
|
2953 #define SIGN(x) ((x < 0)?-1:1) |
|
2954 if (m1) |
|
2955 slope = m2/m1; |
|
2956 else { |
|
2957 //windows doesn't define INFINITY :( |
|
2958 #ifdef INFINITY |
|
2959 slope = INFINITY*SIGN(m2); |
|
2960 #else |
|
2961 if (sizeof(qreal) == sizeof(double)) { |
|
2962 return 1.79769313486231570e+308; |
|
2963 } else { |
|
2964 return ((qreal)3.40282346638528860e+38); |
|
2965 } |
|
2966 #endif |
|
2967 } |
|
2968 |
|
2969 return slope; |
|
2970 } |
|
2971 |
|
2972 /*! |
|
2973 \since 4.4 |
|
2974 |
|
2975 Adds the given rectangle \a rect with rounded corners to the path. |
|
2976 |
|
2977 The \a xRadius and \a yRadius arguments specify the radii of |
|
2978 the ellipses defining the corners of the rounded rectangle. |
|
2979 When \a mode is Qt::RelativeSize, \a xRadius and |
|
2980 \a yRadius are specified in percentage of half the rectangle's |
|
2981 width and height respectively, and should be in the range 0.0 to 100.0. |
|
2982 |
|
2983 \sa addRect() |
|
2984 */ |
|
2985 void QPainterPath::addRoundedRect(const QRectF &rect, qreal xRadius, qreal yRadius, |
|
2986 Qt::SizeMode mode) |
|
2987 { |
|
2988 QRectF r = rect.normalized(); |
|
2989 |
|
2990 if (r.isNull()) |
|
2991 return; |
|
2992 |
|
2993 if (mode == Qt::AbsoluteSize) { |
|
2994 qreal w = r.width() / 2; |
|
2995 qreal h = r.height() / 2; |
|
2996 |
|
2997 if (w == 0) { |
|
2998 xRadius = 0; |
|
2999 } else { |
|
3000 xRadius = 100 * qMin(xRadius, w) / w; |
|
3001 } |
|
3002 if (h == 0) { |
|
3003 yRadius = 0; |
|
3004 } else { |
|
3005 yRadius = 100 * qMin(yRadius, h) / h; |
|
3006 } |
|
3007 } else { |
|
3008 if (xRadius > 100) // fix ranges |
|
3009 xRadius = 100; |
|
3010 |
|
3011 if (yRadius > 100) |
|
3012 yRadius = 100; |
|
3013 } |
|
3014 |
|
3015 if (xRadius <= 0 || yRadius <= 0) { // add normal rectangle |
|
3016 addRect(r); |
|
3017 return; |
|
3018 } |
|
3019 |
|
3020 qreal x = r.x(); |
|
3021 qreal y = r.y(); |
|
3022 qreal w = r.width(); |
|
3023 qreal h = r.height(); |
|
3024 qreal rxx2 = w*xRadius/100; |
|
3025 qreal ryy2 = h*yRadius/100; |
|
3026 |
|
3027 ensureData(); |
|
3028 detach(); |
|
3029 |
|
3030 arcMoveTo(x, y, rxx2, ryy2, 90); |
|
3031 arcTo(x, y, rxx2, ryy2, 90, 90); |
|
3032 arcTo(x, y+h-ryy2, rxx2, ryy2, 2*90, 90); |
|
3033 arcTo(x+w-rxx2, y+h-ryy2, rxx2, ryy2, 3*90, 90); |
|
3034 arcTo(x+w-rxx2, y, rxx2, ryy2, 0, 90); |
|
3035 closeSubpath(); |
|
3036 |
|
3037 d_func()->require_moveTo = true; |
|
3038 } |
|
3039 |
|
3040 /*! |
|
3041 \fn void QPainterPath::addRoundedRect(qreal x, qreal y, qreal w, qreal h, qreal xRadius, qreal yRadius, Qt::SizeMode mode = Qt::AbsoluteSize); |
|
3042 \since 4.4 |
|
3043 \overload |
|
3044 |
|
3045 Adds the given rectangle \a x, \a y, \a w, \a h with rounded corners to the path. |
|
3046 */ |
|
3047 |
|
3048 /*! |
|
3049 \obsolete |
|
3050 |
|
3051 Adds a rectangle \a r with rounded corners to the path. |
|
3052 |
|
3053 The \a xRnd and \a yRnd arguments specify how rounded the corners |
|
3054 should be. 0 is angled corners, 99 is maximum roundedness. |
|
3055 |
|
3056 \sa addRoundedRect() |
|
3057 */ |
|
3058 void QPainterPath::addRoundRect(const QRectF &r, int xRnd, int yRnd) |
|
3059 { |
|
3060 if(xRnd >= 100) // fix ranges |
|
3061 xRnd = 99; |
|
3062 if(yRnd >= 100) |
|
3063 yRnd = 99; |
|
3064 if(xRnd <= 0 || yRnd <= 0) { // add normal rectangle |
|
3065 addRect(r); |
|
3066 return; |
|
3067 } |
|
3068 |
|
3069 QRectF rect = r.normalized(); |
|
3070 |
|
3071 if (rect.isNull()) |
|
3072 return; |
|
3073 |
|
3074 qreal x = rect.x(); |
|
3075 qreal y = rect.y(); |
|
3076 qreal w = rect.width(); |
|
3077 qreal h = rect.height(); |
|
3078 qreal rxx2 = w*xRnd/100; |
|
3079 qreal ryy2 = h*yRnd/100; |
|
3080 |
|
3081 ensureData(); |
|
3082 detach(); |
|
3083 |
|
3084 arcMoveTo(x, y, rxx2, ryy2, 90); |
|
3085 arcTo(x, y, rxx2, ryy2, 90, 90); |
|
3086 arcTo(x, y+h-ryy2, rxx2, ryy2, 2*90, 90); |
|
3087 arcTo(x+w-rxx2, y+h-ryy2, rxx2, ryy2, 3*90, 90); |
|
3088 arcTo(x+w-rxx2, y, rxx2, ryy2, 0, 90); |
|
3089 closeSubpath(); |
|
3090 |
|
3091 d_func()->require_moveTo = true; |
|
3092 } |
|
3093 |
|
3094 /*! |
|
3095 \obsolete |
|
3096 |
|
3097 \fn bool QPainterPath::addRoundRect(const QRectF &rect, int roundness); |
|
3098 \since 4.3 |
|
3099 \overload |
|
3100 |
|
3101 Adds a rounded rectangle, \a rect, to the path. |
|
3102 |
|
3103 The \a roundness argument specifies uniform roundness for the |
|
3104 rectangle. Vertical and horizontal roundness factors will be |
|
3105 adjusted accordingly to act uniformly around both axes. Use this |
|
3106 method if you want a rectangle equally rounded across both the X and |
|
3107 Y axis. |
|
3108 |
|
3109 \sa addRoundedRect() |
|
3110 */ |
|
3111 |
|
3112 /*! |
|
3113 \obsolete |
|
3114 |
|
3115 \fn void QPainterPath::addRoundRect(qreal x, qreal y, qreal w, qreal h, int xRnd, int yRnd); |
|
3116 \overload |
|
3117 |
|
3118 Adds a rectangle with rounded corners to the path. The rectangle |
|
3119 is constructed from \a x, \a y, and the width and height \a w |
|
3120 and \a h. |
|
3121 |
|
3122 The \a xRnd and \a yRnd arguments specify how rounded the corners |
|
3123 should be. 0 is angled corners, 99 is maximum roundedness. |
|
3124 |
|
3125 \sa addRoundedRect() |
|
3126 */ |
|
3127 |
|
3128 /*! |
|
3129 \obsolete |
|
3130 |
|
3131 \fn bool QPainterPath::addRoundRect(qreal x, qreal y, qreal width, qreal height, int roundness); |
|
3132 \since 4.3 |
|
3133 \overload |
|
3134 |
|
3135 Adds a rounded rectangle to the path, defined by the coordinates \a |
|
3136 x and \a y with the specified \a width and \a height. |
|
3137 |
|
3138 The \a roundness argument specifies uniform roundness for the |
|
3139 rectangle. Vertical and horizontal roundness factors will be |
|
3140 adjusted accordingly to act uniformly around both axes. Use this |
|
3141 method if you want a rectangle equally rounded across both the X and |
|
3142 Y axis. |
|
3143 |
|
3144 \sa addRoundedRect() |
|
3145 */ |
|
3146 |
|
3147 /*! |
|
3148 \since 4.3 |
|
3149 |
|
3150 Returns a path which is the union of this path's fill area and \a p's fill area. |
|
3151 |
|
3152 Set operations on paths will treat the paths as areas. Non-closed |
|
3153 paths will be treated as implicitly closed. |
|
3154 |
|
3155 \sa intersected(), subtracted() |
|
3156 */ |
|
3157 QPainterPath QPainterPath::united(const QPainterPath &p) const |
|
3158 { |
|
3159 if (isEmpty() || p.isEmpty()) |
|
3160 return isEmpty() ? p : *this; |
|
3161 QPathClipper clipper(*this, p); |
|
3162 return clipper.clip(QPathClipper::BoolOr); |
|
3163 } |
|
3164 |
|
3165 /*! |
|
3166 \since 4.3 |
|
3167 |
|
3168 Returns a path which is the intersection of this path's fill area and \a p's fill area. |
|
3169 */ |
|
3170 QPainterPath QPainterPath::intersected(const QPainterPath &p) const |
|
3171 { |
|
3172 if (isEmpty() || p.isEmpty()) |
|
3173 return QPainterPath(); |
|
3174 QPathClipper clipper(*this, p); |
|
3175 return clipper.clip(QPathClipper::BoolAnd); |
|
3176 } |
|
3177 |
|
3178 /*! |
|
3179 \since 4.3 |
|
3180 |
|
3181 Returns a path which is \a p's fill area subtracted from this path's fill area. |
|
3182 |
|
3183 Set operations on paths will treat the paths as areas. Non-closed |
|
3184 paths will be treated as implicitly closed. |
|
3185 |
|
3186 */ |
|
3187 QPainterPath QPainterPath::subtracted(const QPainterPath &p) const |
|
3188 { |
|
3189 if (isEmpty() || p.isEmpty()) |
|
3190 return *this; |
|
3191 QPathClipper clipper(*this, p); |
|
3192 return clipper.clip(QPathClipper::BoolSub); |
|
3193 } |
|
3194 |
|
3195 /*! |
|
3196 \since 4.3 |
|
3197 \obsolete |
|
3198 |
|
3199 Use subtracted() instead. |
|
3200 |
|
3201 \sa subtracted() |
|
3202 */ |
|
3203 QPainterPath QPainterPath::subtractedInverted(const QPainterPath &p) const |
|
3204 { |
|
3205 return p.subtracted(*this); |
|
3206 } |
|
3207 |
|
3208 /*! |
|
3209 \since 4.4 |
|
3210 |
|
3211 Returns a simplified version of this path. This implies merging all subpaths that intersect, |
|
3212 and returning a path containing no intersecting edges. Consecutive parallel lines will also |
|
3213 be merged. The simplified path will always use the default fill rule, Qt::OddEvenFill. |
|
3214 */ |
|
3215 QPainterPath QPainterPath::simplified() const |
|
3216 { |
|
3217 if(isEmpty()) |
|
3218 return *this; |
|
3219 QPathClipper clipper(*this, QPainterPath()); |
|
3220 return clipper.clip(QPathClipper::Simplify); |
|
3221 } |
|
3222 |
|
3223 /*! |
|
3224 \since 4.3 |
|
3225 |
|
3226 Returns true if the current path intersects at any point the given path \a p. |
|
3227 Also returns true if the current path contains or is contained by any part of \a p. |
|
3228 |
|
3229 Set operations on paths will treat the paths as areas. Non-closed |
|
3230 paths will be treated as implicitly closed. |
|
3231 |
|
3232 \sa contains() |
|
3233 */ |
|
3234 bool QPainterPath::intersects(const QPainterPath &p) const |
|
3235 { |
|
3236 if (p.elementCount() == 1) |
|
3237 return contains(p.elementAt(0)); |
|
3238 if (isEmpty() || p.isEmpty()) |
|
3239 return false; |
|
3240 QPathClipper clipper(*this, p); |
|
3241 return clipper.intersect(); |
|
3242 } |
|
3243 |
|
3244 /*! |
|
3245 \since 4.3 |
|
3246 |
|
3247 Returns true if the given path \a p is contained within |
|
3248 the current path. Returns false if any edges of the current path and |
|
3249 \a p intersect. |
|
3250 |
|
3251 Set operations on paths will treat the paths as areas. Non-closed |
|
3252 paths will be treated as implicitly closed. |
|
3253 |
|
3254 \sa intersects() |
|
3255 */ |
|
3256 bool QPainterPath::contains(const QPainterPath &p) const |
|
3257 { |
|
3258 if (p.elementCount() == 1) |
|
3259 return contains(p.elementAt(0)); |
|
3260 if (isEmpty() || p.isEmpty()) |
|
3261 return false; |
|
3262 QPathClipper clipper(*this, p); |
|
3263 return clipper.contains(); |
|
3264 } |
|
3265 |
|
3266 void QPainterPath::setDirty(bool dirty) |
|
3267 { |
|
3268 d_func()->dirtyBounds = dirty; |
|
3269 d_func()->dirtyControlBounds = dirty; |
|
3270 delete d_func()->pathConverter; |
|
3271 d_func()->pathConverter = 0; |
|
3272 } |
|
3273 |
|
3274 void QPainterPath::computeBoundingRect() const |
|
3275 { |
|
3276 QPainterPathData *d = d_func(); |
|
3277 d->dirtyBounds = false; |
|
3278 if (!d_ptr) { |
|
3279 d->bounds = QRect(); |
|
3280 return; |
|
3281 } |
|
3282 |
|
3283 qreal minx, maxx, miny, maxy; |
|
3284 minx = maxx = d->elements.at(0).x; |
|
3285 miny = maxy = d->elements.at(0).y; |
|
3286 for (int i=1; i<d->elements.size(); ++i) { |
|
3287 const Element &e = d->elements.at(i); |
|
3288 |
|
3289 switch (e.type) { |
|
3290 case MoveToElement: |
|
3291 case LineToElement: |
|
3292 if (e.x > maxx) maxx = e.x; |
|
3293 else if (e.x < minx) minx = e.x; |
|
3294 if (e.y > maxy) maxy = e.y; |
|
3295 else if (e.y < miny) miny = e.y; |
|
3296 break; |
|
3297 case CurveToElement: |
|
3298 { |
|
3299 QBezier b = QBezier::fromPoints(d->elements.at(i-1), |
|
3300 e, |
|
3301 d->elements.at(i+1), |
|
3302 d->elements.at(i+2)); |
|
3303 QRectF r = qt_painterpath_bezier_extrema(b); |
|
3304 qreal right = r.right(); |
|
3305 qreal bottom = r.bottom(); |
|
3306 if (r.x() < minx) minx = r.x(); |
|
3307 if (right > maxx) maxx = right; |
|
3308 if (r.y() < miny) miny = r.y(); |
|
3309 if (bottom > maxy) maxy = bottom; |
|
3310 i += 2; |
|
3311 } |
|
3312 break; |
|
3313 default: |
|
3314 break; |
|
3315 } |
|
3316 } |
|
3317 d->bounds = QRectF(minx, miny, maxx - minx, maxy - miny); |
|
3318 } |
|
3319 |
|
3320 |
|
3321 void QPainterPath::computeControlPointRect() const |
|
3322 { |
|
3323 QPainterPathData *d = d_func(); |
|
3324 d->dirtyControlBounds = false; |
|
3325 if (!d_ptr) { |
|
3326 d->controlBounds = QRect(); |
|
3327 return; |
|
3328 } |
|
3329 |
|
3330 qreal minx, maxx, miny, maxy; |
|
3331 minx = maxx = d->elements.at(0).x; |
|
3332 miny = maxy = d->elements.at(0).y; |
|
3333 for (int i=1; i<d->elements.size(); ++i) { |
|
3334 const Element &e = d->elements.at(i); |
|
3335 if (e.x > maxx) maxx = e.x; |
|
3336 else if (e.x < minx) minx = e.x; |
|
3337 if (e.y > maxy) maxy = e.y; |
|
3338 else if (e.y < miny) miny = e.y; |
|
3339 } |
|
3340 d->controlBounds = QRectF(minx, miny, maxx - minx, maxy - miny); |
|
3341 } |
|
3342 |
|
3343 #ifndef QT_NO_DEBUG_STREAM |
|
3344 QDebug operator<<(QDebug s, const QPainterPath &p) |
|
3345 { |
|
3346 s.nospace() << "QPainterPath: Element count=" << p.elementCount() << endl; |
|
3347 const char *types[] = {"MoveTo", "LineTo", "CurveTo", "CurveToData"}; |
|
3348 for (int i=0; i<p.elementCount(); ++i) { |
|
3349 s.nospace() << " -> " << types[p.elementAt(i).type] << "(x=" << p.elementAt(i).x << ", y=" << p.elementAt(i).y << ')' << endl; |
|
3350 |
|
3351 } |
|
3352 return s; |
|
3353 } |
|
3354 #endif |
|
3355 |
|
3356 QT_END_NAMESPACE |