1 /*
2 * Copyright (C) 2007-2009 Torch Mobile, Inc.
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Library General Public
6 * License as published by the Free Software Foundation; either
7 * version 2 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Library General Public License for more details.
13 *
14 * You should have received a copy of the GNU Library General Public License
15 * along with this library; see the file COPYING.LIB. If not, write to
16 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18 */
19
20 #include "config.h"
21 #include "PlatformPathWince.h"
22
23 #include "AffineTransform.h"
24 #include "FloatRect.h"
25 #include "GraphicsContext.h"
26 #include "Path.h"
27 #include "PlatformString.h"
28 #include "WinceGraphicsExtras.h"
29 #include <wtf/MathExtras.h>
30 #include <wtf/OwnPtr.h>
31
32 #include <windows.h>
33
34 namespace WebCore {
35
36 // Implemented in GraphicsContextWince.cpp
37 void getEllipsePointByAngle(double angle, double a, double b, float& x, float& y);
38
quadCurve(int segments,Vector<PathPoint> & pts,const PathPoint * control)39 static void quadCurve(int segments, Vector<PathPoint>& pts, const PathPoint* control)
40 {
41 const float step = 1.0 / segments;
42 register float tA = 0.0;
43 register float tB = 1.0;
44
45 float c1x = control[0].x();
46 float c1y = control[0].y();
47 float c2x = control[1].x();
48 float c2y = control[1].y();
49 float c3x = control[2].x();
50 float c3y = control[2].y();
51
52 const int offset = pts.size();
53 pts.resize(offset + segments);
54 PathPoint pp;
55 pp.m_x = c1x;
56 pp.m_y = c1y;
57
58 for (int i = 1; i < segments; ++i) {
59 tA += step;
60 tB -= step;
61
62 const float a = tB * tB;
63 const float b = 2.0 * tA * tB;
64 const float c = tA * tA;
65
66 pp.m_x = c1x * a + c2x * b + c3x * c;
67 pp.m_y = c1y * a + c2y * b + c3y * c;
68
69 pts[offset + i - 1] = pp;
70 }
71
72 pp.m_x = c3x;
73 pp.m_y = c3y;
74 pts[offset + segments - 1] = pp;
75 }
76
bezier(int segments,Vector<PathPoint> & pts,const PathPoint * control)77 static inline void bezier(int segments, Vector<PathPoint>& pts, const PathPoint* control)
78 {
79 const float step = 1.0 / segments;
80 register float tA = 0.0;
81 register float tB = 1.0;
82
83 float c1x = control[0].x();
84 float c1y = control[0].y();
85 float c2x = control[1].x();
86 float c2y = control[1].y();
87 float c3x = control[2].x();
88 float c3y = control[2].y();
89 float c4x = control[3].x();
90 float c4y = control[3].y();
91
92 const int offset = pts.size();
93 pts.resize(offset + segments);
94 PathPoint pp;
95 pp.m_x = c1x;
96 pp.m_y = c1y;
97
98 for (int i = 1; i < segments; ++i) {
99 tA += step;
100 tB -= step;
101 const float tAsq = tA * tA;
102 const float tBsq = tB * tB;
103
104 const float a = tBsq * tB;
105 const float b = 3.0 * tA * tBsq;
106 const float c = 3.0 * tB * tAsq;
107 const float d = tAsq * tA;
108
109 pp.m_x = c1x * a + c2x * b + c3x * c + c4x * d;
110 pp.m_y = c1y * a + c2y * b + c3y * c + c4y * d;
111
112 pts[offset + i - 1] = pp;
113 }
114
115 pp.m_x = c4x;
116 pp.m_y = c4y;
117 pts[offset + segments - 1] = pp;
118 }
119
containsPoint(const FloatRect & r,const FloatPoint & p)120 static bool containsPoint(const FloatRect& r, const FloatPoint& p)
121 {
122 return p.x() >= r.x() && p.y() >= r.y() && p.x() < r.right() && p.y() < r.bottom();
123 }
124
normalizeAngle(float & angle)125 static void normalizeAngle(float& angle)
126 {
127 angle = fmod(angle, 2 * piFloat);
128 if (angle < 0)
129 angle += 2 * piFloat;
130 if (angle < 0.00001f)
131 angle = 0;
132 }
133
transformArcPoint(float & x,float & y,const FloatPoint & c)134 static void transformArcPoint(float& x, float& y, const FloatPoint& c)
135 {
136 x += c.x();
137 y += c.y();
138 }
139
inflateRectToContainPoint(FloatRect & r,float x,float y)140 static void inflateRectToContainPoint(FloatRect& r, float x, float y)
141 {
142 if (r.isEmpty()) {
143 r.setX(x);
144 r.setY(y);
145 r.setSize(FloatSize(1, 1));
146 return;
147 }
148 if (x < r.x()) {
149 r.setWidth(r.right() - x);
150 r.setX(x);
151 } else {
152 float w = x - r.x() + 1;
153 if (w > r.width())
154 r.setWidth(w);
155 }
156 if (y < r.y()) {
157 r.setHeight(r.bottom() - y);
158 r.setY(y);
159 } else {
160 float h = y - r.y() + 1;
161 if (h > r.height())
162 r.setHeight(h);
163 }
164 }
165
166 // return 0-based value: 0 - first Quadrant ( 0 - 90 degree)
quadrant(const PathPoint & point,const PathPoint & origin)167 static inline int quadrant(const PathPoint& point, const PathPoint& origin)
168 {
169 return point.m_x < origin.m_x ?
170 (point.m_y < origin.m_y ? 2 : 1)
171 : (point.m_y < origin.m_y ? 3 : 0);
172 }
173
isQuadrantOnLeft(int q)174 static inline bool isQuadrantOnLeft(int q) { return q == 1 || q == 2; }
isQuadrantOnRight(int q)175 static inline bool isQuadrantOnRight(int q) { return q == 0 || q == 3; }
isQuadrantOnTop(int q)176 static inline bool isQuadrantOnTop(int q) { return q == 2 || q == 3; }
isQuadrantOnBottom(int q)177 static inline bool isQuadrantOnBottom(int q) { return q == 0 || q == 1; }
178
nextQuadrant(int q)179 static inline int nextQuadrant(int q) { return q == 3 ? 0 : q + 1; }
quadrantDiff(int q1,int q2)180 static inline int quadrantDiff(int q1, int q2)
181 {
182 int d = q1 - q2;
183 while (d < 0)
184 d += 4;
185 return d;
186 }
187
188 struct PathVector {
189 float m_x;
190 float m_y;
191
PathVectorWebCore::PathVector192 PathVector() : m_x(0), m_y(0) {}
PathVectorWebCore::PathVector193 PathVector(float x, float y) : m_x(x), m_y(y) {}
angleWebCore::PathVector194 double angle() const { return atan2(m_y, m_x); }
operator doubleWebCore::PathVector195 operator double () const { return angle(); }
lengthWebCore::PathVector196 double length() const { return _hypot(m_x, m_y); }
197 };
198
operator -(const PathPoint & p1,const PathPoint & p2)199 PathVector operator-(const PathPoint& p1, const PathPoint& p2)
200 {
201 return PathVector(p1.m_x - p2.m_x, p1.m_y - p2.m_y);
202 }
203
addArcPoint(PathPolygon & poly,const PathPoint & center,const PathPoint & radius,double angle)204 static void addArcPoint(PathPolygon& poly, const PathPoint& center, const PathPoint& radius, double angle)
205 {
206 PathPoint p;
207 getEllipsePointByAngle(angle, radius.m_x, radius.m_y, p.m_x, p.m_y);
208 transformArcPoint(p.m_x, p.m_y, center);
209 if (poly.isEmpty() || poly.last() != p)
210 poly.append(p);
211 }
212
addArcPoints(PathPolygon & poly,const PlatformPathElement::ArcTo & data)213 static void addArcPoints(PathPolygon& poly, const PlatformPathElement::ArcTo& data)
214 {
215 const PathPoint& startPoint = poly.last();
216 double curAngle = startPoint - data.m_center;
217 double endAngle = data.m_end - data.m_center;
218 double angleStep = 2. / std::max(data.m_radius.m_x, data.m_radius.m_y);
219 if (data.m_clockwise) {
220 if (endAngle <= curAngle || startPoint == data.m_end)
221 endAngle += 2 * piDouble;
222 } else {
223 angleStep = -angleStep;
224 if (endAngle >= curAngle || startPoint == data.m_end)
225 endAngle -= 2 * piDouble;
226 }
227
228 for (curAngle += angleStep; data.m_clockwise ? curAngle < endAngle : curAngle > endAngle; curAngle += angleStep)
229 addArcPoint(poly, data.m_center, data.m_radius, curAngle);
230
231 if (poly.isEmpty() || poly.last() != data.m_end)
232 poly.append(data.m_end);
233 }
234
drawPolygons(HDC dc,const Vector<PathPolygon> & polygons,bool fill,const AffineTransform * transformation)235 static void drawPolygons(HDC dc, const Vector<PathPolygon>& polygons, bool fill, const AffineTransform* transformation)
236 {
237 MemoryAllocationCanFail canFail;
238 for (Vector<PathPolygon>::const_iterator i = polygons.begin(); i != polygons.end(); ++i) {
239 int npoints = i->size();
240 if (!npoints)
241 continue;
242
243 POINT* winPoints = 0;
244 if (fill) {
245 if (npoints > 2)
246 winPoints = new POINT[npoints + 1];
247 } else
248 winPoints = new POINT[npoints];
249
250 if (winPoints) {
251 if (transformation) {
252 for (int i2 = 0; i2 < npoints; ++i2) {
253 FloatPoint trPoint = transformation->mapPoint(i->at(i2));
254 winPoints[i2].x = stableRound(trPoint.x());
255 winPoints[i2].y = stableRound(trPoint.y());
256 }
257 } else {
258 for (int i2 = 0; i2 < npoints; ++i2) {
259 winPoints[i2].x = stableRound(i->at(i2).x());
260 winPoints[i2].y = stableRound(i->at(i2).y());
261 }
262 }
263
264 if (fill && winPoints[npoints - 1] != winPoints[0]) {
265 winPoints[npoints].x = winPoints[0].x;
266 winPoints[npoints].y = winPoints[0].y;
267 ++npoints;
268 }
269
270 if (fill)
271 ::Polygon(dc, winPoints, npoints);
272 else
273 ::Polyline(dc, winPoints, npoints);
274 delete[] winPoints;
275 }
276 }
277 }
278
279
numControlPoints() const280 int PlatformPathElement::numControlPoints() const
281 {
282 switch (m_type) {
283 case PathMoveTo:
284 case PathLineTo:
285 return 1;
286 case PathQuadCurveTo:
287 case PathArcTo:
288 return 2;
289 case PathBezierCurveTo:
290 return 3;
291 default:
292 ASSERT(m_type == PathCloseSubpath);
293 return 0;
294 }
295 }
296
numPoints() const297 int PlatformPathElement::numPoints() const
298 {
299 switch (m_type) {
300 case PathMoveTo:
301 case PathLineTo:
302 case PathArcTo:
303 return 1;
304 case PathQuadCurveTo:
305 return 2;
306 case PathBezierCurveTo:
307 return 3;
308 default:
309 ASSERT(m_type == PathCloseSubpath);
310 return 0;
311 }
312 }
313
move(const FloatSize & offset)314 void PathPolygon::move(const FloatSize& offset)
315 {
316 for (Vector<PathPoint>::iterator i = begin(); i < end(); ++i)
317 i->move(offset);
318 }
319
transform(const AffineTransform & t)320 void PathPolygon::transform(const AffineTransform& t)
321 {
322 for (Vector<PathPoint>::iterator i = begin(); i < end(); ++i)
323 *i = t.mapPoint(*i);
324 }
325
contains(const FloatPoint & point) const326 bool PathPolygon::contains(const FloatPoint& point) const
327 {
328 if (size() < 3)
329 return false;
330
331 // Test intersections between the polygon and the vertical line: x = point.x()
332
333 int intersected = 0;
334 const PathPoint* point1 = &last();
335 Vector<PathPoint>::const_iterator last = end();
336 // wasNegative: -1 means unknown, 0 means false, 1 means true.
337 int wasNegative = -1;
338 for (Vector<PathPoint>::const_iterator i = begin(); i != last; ++i) {
339 const PathPoint& point2 = *i;
340 if (point1->x() != point.x()) {
341 if (point2.x() == point.x()) {
342 // We are getting on the vertical line
343 wasNegative = point1->x() < point.x() ? 1 : 0;
344 } else if (point2.x() < point.x() != point1->x() < point.x()) {
345 float y = (point2.y() - point1->y()) / (point2.x() - point1->x()) * (point.x() - point1->x()) + point1->y();
346 if (y >= point.y())
347 ++intersected;
348 }
349 } else {
350 // We were on the vertical line
351
352 // handle special case
353 if (point1->y() == point.y())
354 return true;
355
356 if (point1->y() > point.y()) {
357 if (point2.x() == point.x()) {
358 // see if the point is on this segment
359 if (point2.y() <= point.y())
360 return true;
361
362 // We are still on the line
363 } else {
364 // We are leaving the line now.
365 // We have to get back to see which side we come from. If we come from
366 // the same side we are leaving, no intersection should be counted
367 if (wasNegative < 0) {
368 Vector<PathPoint>::const_iterator jLast = i;
369 Vector<PathPoint>::const_iterator j = i;
370 do {
371 if (j == begin())
372 j = last;
373 else
374 --j;
375 if (j->x() != point.x()) {
376 if (j->x() > point.x())
377 wasNegative = 0;
378 else
379 wasNegative = 1;
380 break;
381 }
382 } while (j != jLast);
383
384 if (wasNegative < 0)
385 return false;
386 }
387 if (wasNegative ? point2.x() > point.x() : point2.x() < point.x())
388 ++intersected;
389 }
390 } else if (point2.x() == point.x() && point2.y() >= point.y())
391 return true;
392 }
393 point1 = &point2;
394 }
395
396 return intersected & 1;
397 }
398
move(const FloatSize & offset)399 void PlatformPathElement::move(const FloatSize& offset)
400 {
401 int n = numControlPoints();
402 for (int i = 0; i < n; ++i)
403 m_data.m_points[i].move(offset);
404 }
405
transform(const AffineTransform & t)406 void PlatformPathElement::transform(const AffineTransform& t)
407 {
408 int n = numControlPoints();
409 for (int i = 0; i < n; ++i) {
410 FloatPoint p = t.mapPoint(m_data.m_points[i]);
411 m_data.m_points[i].set(p.x(), p.y());
412 }
413 }
414
inflateRectToContainMe(FloatRect & r,const FloatPoint & lastPoint) const415 void PlatformPathElement::inflateRectToContainMe(FloatRect& r, const FloatPoint& lastPoint) const
416 {
417 if (m_type == PathArcTo) {
418 const ArcTo& data = m_data.m_arcToData;
419 PathPoint startPoint;
420 startPoint = lastPoint;
421 PathPoint endPoint = data.m_end;
422 if (!data.m_clockwise)
423 std::swap(startPoint, endPoint);
424
425 int q0 = quadrant(startPoint, data.m_center);
426 int q1 = quadrant(endPoint, data.m_center);
427 bool containsExtremes[4] = { false }; // bottom, left, top, right
428 static const PathPoint extremeVectors[4] = { { 0, 1 }, { -1, 0 }, { 0, -1 }, { 1, 0 } };
429 if (q0 == q1) {
430 if (startPoint.m_x == endPoint.m_x || isQuadrantOnBottom(q0) != startPoint.m_x > endPoint.m_x) {
431 for (int i = 0; i < 4; ++i)
432 containsExtremes[i] = true;
433 }
434 } else {
435 int extreme = q0;
436 int diff = quadrantDiff(q1, q0);
437 for (int i = 0; i < diff; ++i) {
438 containsExtremes[extreme] = true;
439 extreme = nextQuadrant(extreme);
440 }
441 }
442
443 inflateRectToContainPoint(r, startPoint.m_x, startPoint.m_y);
444 inflateRectToContainPoint(r, endPoint.m_x, endPoint.m_y);
445 for (int i = 0; i < 4; ++i) {
446 if (containsExtremes[i])
447 inflateRectToContainPoint(r, data.m_center.m_x + data.m_radius.m_x * extremeVectors[i].m_x, data.m_center.m_y + data.m_radius.m_y * extremeVectors[i].m_y);
448 }
449 } else {
450 int n = numPoints();
451 for (int i = 0; i < n; ++i)
452 inflateRectToContainPoint(r, m_data.m_points[i].m_x, m_data.m_points[i].m_y);
453 }
454 }
455
type() const456 PathElementType PlatformPathElement::type() const
457 {
458 switch (m_type) {
459 case PathMoveTo:
460 return PathElementMoveToPoint;
461 case PathLineTo:
462 return PathElementAddLineToPoint;
463 case PathArcTo:
464 // FIXME: there's no arcTo type for PathElement
465 return PathElementAddLineToPoint;
466 // return PathElementAddQuadCurveToPoint;
467 case PathQuadCurveTo:
468 return PathElementAddQuadCurveToPoint;
469 case PathBezierCurveTo:
470 return PathElementAddCurveToPoint;
471 default:
472 ASSERT(m_type == PathCloseSubpath);
473 return PathElementCloseSubpath;
474 }
475 }
476
PlatformPath()477 PlatformPath::PlatformPath()
478 : m_penLifted(true)
479 {
480 m_currentPoint.clear();
481 }
482
ensureSubpath()483 void PlatformPath::ensureSubpath()
484 {
485 if (m_penLifted) {
486 m_penLifted = false;
487 m_subpaths.append(PathPolygon());
488 m_subpaths.last().append(m_currentPoint);
489 } else
490 ASSERT(!m_subpaths.isEmpty());
491 }
492
addToSubpath(const PlatformPathElement & e)493 void PlatformPath::addToSubpath(const PlatformPathElement& e)
494 {
495 if (e.platformType() == PlatformPathElement::PathMoveTo) {
496 m_penLifted = true;
497 m_currentPoint = e.pointAt(0);
498 } else if (e.platformType() == PlatformPathElement::PathCloseSubpath) {
499 m_penLifted = true;
500 if (!m_subpaths.isEmpty()) {
501 if (m_currentPoint != m_subpaths.last()[0]) {
502 // According to W3C, we have to draw a line from current point to the initial point
503 m_subpaths.last().append(m_subpaths.last()[0]);
504 m_currentPoint = m_subpaths.last()[0];
505 }
506 } else
507 m_currentPoint.clear();
508 } else {
509 ensureSubpath();
510 switch (e.platformType()) {
511 case PlatformPathElement::PathLineTo:
512 m_subpaths.last().append(e.pointAt(0));
513 break;
514 case PlatformPathElement::PathArcTo:
515 addArcPoints(m_subpaths.last(), e.arcTo());
516 break;
517 case PlatformPathElement::PathQuadCurveTo:
518 {
519 PathPoint control[] = {
520 m_currentPoint,
521 e.pointAt(0),
522 e.pointAt(1),
523 };
524 // FIXME: magic number?
525 quadCurve(50, m_subpaths.last(), control);
526 }
527 break;
528 case PlatformPathElement::PathBezierCurveTo:
529 {
530 PathPoint control[] = {
531 m_currentPoint,
532 e.pointAt(0),
533 e.pointAt(1),
534 e.pointAt(2),
535 };
536 // FIXME: magic number?
537 bezier(100, m_subpaths.last(), control);
538 }
539 break;
540 default:
541 ASSERT_NOT_REACHED();
542 break;
543 }
544 m_currentPoint = m_subpaths.last().last();
545 }
546 }
547
append(const PlatformPathElement & e)548 void PlatformPath::append(const PlatformPathElement& e)
549 {
550 e.inflateRectToContainMe(m_boundingRect, lastPoint());
551 addToSubpath(e);
552 m_elements.append(e);
553 }
554
append(const PlatformPath & p)555 void PlatformPath::append(const PlatformPath& p)
556 {
557 const PlatformPathElements& e = p.elements();
558 for (PlatformPathElements::const_iterator it(e.begin()); it != e.end(); ++it) {
559 addToSubpath(*it);
560 it->inflateRectToContainMe(m_boundingRect, lastPoint());
561 m_elements.append(*it);
562 }
563 }
564
clear()565 void PlatformPath::clear()
566 {
567 m_elements.clear();
568 m_boundingRect = FloatRect();
569 m_subpaths.clear();
570 m_currentPoint.clear();
571 m_penLifted = true;
572 }
573
strokePath(HDC dc,const AffineTransform * transformation) const574 void PlatformPath::strokePath(HDC dc, const AffineTransform* transformation) const
575 {
576 drawPolygons(dc, m_subpaths, false, transformation);
577 }
578
fillPath(HDC dc,const AffineTransform * transformation) const579 void PlatformPath::fillPath(HDC dc, const AffineTransform* transformation) const
580 {
581 HGDIOBJ oldPen = SelectObject(dc, GetStockObject(NULL_PEN));
582 drawPolygons(dc, m_subpaths, true, transformation);
583 SelectObject(dc, oldPen);
584 }
585
translate(const FloatSize & size)586 void PlatformPath::translate(const FloatSize& size)
587 {
588 for (PlatformPathElements::iterator it(m_elements.begin()); it != m_elements.end(); ++it)
589 it->move(size);
590
591 m_boundingRect.move(size);
592 for (Vector<PathPolygon>::iterator it = m_subpaths.begin(); it != m_subpaths.end(); ++it)
593 it->move(size);
594 }
595
transform(const AffineTransform & t)596 void PlatformPath::transform(const AffineTransform& t)
597 {
598 for (PlatformPathElements::iterator it(m_elements.begin()); it != m_elements.end(); ++it)
599 it->transform(t);
600
601 m_boundingRect = t.mapRect(m_boundingRect);
602 for (Vector<PathPolygon>::iterator it = m_subpaths.begin(); it != m_subpaths.end(); ++it)
603 it->transform(t);
604 }
605
contains(const FloatPoint & point,WindRule rule) const606 bool PlatformPath::contains(const FloatPoint& point, WindRule rule) const
607 {
608 // optimization: check the bounding rect first
609 if (!containsPoint(m_boundingRect, point))
610 return false;
611
612 for (Vector<PathPolygon>::const_iterator i = m_subpaths.begin(); i != m_subpaths.end(); ++i) {
613 if (i->contains(point))
614 return true;
615 }
616
617 return false;
618 }
619
moveTo(const FloatPoint & point)620 void PlatformPath::moveTo(const FloatPoint& point)
621 {
622 PlatformPathElement::MoveTo data = { { point.x(), point.y() } };
623 PlatformPathElement pe(data);
624 append(pe);
625 }
626
addLineTo(const FloatPoint & point)627 void PlatformPath::addLineTo(const FloatPoint& point)
628 {
629 PlatformPathElement::LineTo data = { { point.x(), point.y() } };
630 PlatformPathElement pe(data);
631 append(pe);
632 }
633
addQuadCurveTo(const FloatPoint & cp,const FloatPoint & p)634 void PlatformPath::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& p)
635 {
636 PlatformPathElement::QuadCurveTo data = { { cp.x(), cp.y() }, { p.x(), p.y() } };
637 PlatformPathElement pe(data);
638 append(pe);
639 }
640
addBezierCurveTo(const FloatPoint & cp1,const FloatPoint & cp2,const FloatPoint & p)641 void PlatformPath::addBezierCurveTo(const FloatPoint& cp1, const FloatPoint& cp2, const FloatPoint& p)
642 {
643 PlatformPathElement::BezierCurveTo data = { { cp1.x(), cp1.y() }, { cp2.x(), cp2.y() }, { p.x(), p.y() } };
644 PlatformPathElement pe(data);
645 append(pe);
646 }
647
addArcTo(const FloatPoint & fp1,const FloatPoint & fp2,float radius)648 void PlatformPath::addArcTo(const FloatPoint& fp1, const FloatPoint& fp2, float radius)
649 {
650 const PathPoint& p0 = m_currentPoint;
651 PathPoint p1;
652 p1 = fp1;
653 PathPoint p2;
654 p2 = fp2;
655 if (!radius || p0 == p1 || p1 == p2) {
656 addLineTo(p1);
657 return;
658 }
659
660 PathVector v01 = p0 - p1;
661 PathVector v21 = p2 - p1;
662
663 // sin(A - B) = sin(A) * cos(B) - sin(B) * cos(A)
664 double cross = v01.m_x * v21.m_y - v01.m_y * v21.m_x;
665
666 if (fabs(cross) < 1E-10) {
667 // on one line
668 addLineTo(p1);
669 return;
670 }
671
672 double d01 = v01.length();
673 double d21 = v21.length();
674 double angle = (piDouble - abs(asin(cross / (d01 * d21)))) * 0.5;
675 double span = radius * tan(angle);
676 double rate = span / d01;
677 PathPoint startPoint;
678 startPoint.m_x = p1.m_x + v01.m_x * rate;
679 startPoint.m_y = p1.m_y + v01.m_y * rate;
680
681 addLineTo(startPoint);
682
683 PathPoint endPoint;
684 rate = span / d21;
685 endPoint.m_x = p1.m_x + v21.m_x * rate;
686 endPoint.m_y = p1.m_y + v21.m_y * rate;
687
688 PathPoint midPoint;
689 midPoint.m_x = (startPoint.m_x + endPoint.m_x) * 0.5;
690 midPoint.m_y = (startPoint.m_y + endPoint.m_y) * 0.5;
691
692 PathVector vm1 = midPoint - p1;
693 double dm1 = vm1.length();
694 double d = _hypot(radius, span);
695
696 PathPoint centerPoint;
697 rate = d / dm1;
698 centerPoint.m_x = p1.m_x + vm1.m_x * rate;
699 centerPoint.m_y = p1.m_y + vm1.m_y * rate;
700
701 PlatformPathElement::ArcTo data = {
702 endPoint,
703 centerPoint,
704 { radius, radius },
705 cross < 0
706 };
707 PlatformPathElement pe(data);
708 append(pe);
709 }
710
closeSubpath()711 void PlatformPath::closeSubpath()
712 {
713 PlatformPathElement pe;
714 append(pe);
715 }
716
717 // add a circular arc centred at p with radius r from start angle sar (radians) to end angle ear
addEllipse(const FloatPoint & p,float a,float b,float sar,float ear,bool anticlockwise)718 void PlatformPath::addEllipse(const FloatPoint& p, float a, float b, float sar, float ear, bool anticlockwise)
719 {
720 float startX, startY, endX, endY;
721
722 normalizeAngle(sar);
723 normalizeAngle(ear);
724
725 getEllipsePointByAngle(sar, a, b, startX, startY);
726 getEllipsePointByAngle(ear, a, b, endX, endY);
727
728 transformArcPoint(startX, startY, p);
729 transformArcPoint(endX, endY, p);
730
731 FloatPoint start(startX, startY);
732 moveTo(start);
733
734 PlatformPathElement::ArcTo data = { { endX, endY }, { p.x(), p.y() }, { a, b }, !anticlockwise };
735 PlatformPathElement pe(data);
736 append(pe);
737 }
738
739
addRect(const FloatRect & r)740 void PlatformPath::addRect(const FloatRect& r)
741 {
742 moveTo(r.location());
743
744 float right = r.right() - 1;
745 float bottom = r.bottom() - 1;
746 addLineTo(FloatPoint(right, r.y()));
747 addLineTo(FloatPoint(right, bottom));
748 addLineTo(FloatPoint(r.x(), bottom));
749 addLineTo(r.location());
750 }
751
addEllipse(const FloatRect & r)752 void PlatformPath::addEllipse(const FloatRect& r)
753 {
754 FloatSize radius(r.width() * 0.5, r.height() * 0.5);
755 addEllipse(r.location() + radius, radius.width(), radius.height(), 0, 0, true);
756 }
757
debugString() const758 String PlatformPath::debugString() const
759 {
760 String ret;
761 for (PlatformPathElements::const_iterator i(m_elements.begin()); i != m_elements.end(); ++i) {
762 switch (i->platformType()) {
763 case PlatformPathElement::PathMoveTo:
764 case PlatformPathElement::PathLineTo:
765 ret += String::format("M %f %f\n", i->pointAt(0).m_x, i->pointAt(0).m_y);
766 break;
767 case PlatformPathElement::PathArcTo:
768 ret += String::format("A %f %f %f %f %f %f %c\n"
769 , i->arcTo().m_end.m_x, i->arcTo().m_end.m_y
770 , i->arcTo().m_center.m_x, i->arcTo().m_center.m_y
771 , i->arcTo().m_radius.m_x, i->arcTo().m_radius.m_y
772 , i->arcTo().m_clockwise? 'Y' : 'N');
773 break;
774 case PlatformPathElement::PathQuadCurveTo:
775 ret += String::format("Q %f %f %f %f\n"
776 , i->pointAt(0).m_x, i->pointAt(0).m_y
777 , i->pointAt(1).m_x, i->pointAt(1).m_y);
778 break;
779 case PlatformPathElement::PathBezierCurveTo:
780 ret += String::format("B %f %f %f %f %f %f\n"
781 , i->pointAt(0).m_x, i->pointAt(0).m_y
782 , i->pointAt(1).m_x, i->pointAt(1).m_y
783 , i->pointAt(2).m_x, i->pointAt(2).m_y);
784 break;
785 default:
786 ASSERT(i->platformType() == PlatformPathElement::PathCloseSubpath);
787 ret += "S\n";
788 break;
789 }
790 }
791
792 return ret;
793 }
794
apply(void * info,PathApplierFunction function) const795 void PlatformPath::apply(void* info, PathApplierFunction function) const
796 {
797 PathElement pelement;
798 FloatPoint points[3];
799 pelement.points = points;
800
801 for (PlatformPathElements::const_iterator it(m_elements.begin()); it != m_elements.end(); ++it) {
802 pelement.type = it->type();
803 int n = it->numPoints();
804 for (int i = 0; i < n; ++i)
805 points[i] = it->pointAt(i);
806 function(info, &pelement);
807 }
808 }
809
810 } // namespace Webcore
811