• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.maxX() && p.y() < r.maxY();
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.maxX() - 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.maxY() - 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     for (Vector<PathPolygon>::const_iterator i = polygons.begin(); i != polygons.end(); ++i) {
238         int npoints = i->size();
239         if (!npoints)
240             continue;
241 
242         POINT* winPoints = 0;
243         if (fill) {
244             if (npoints > 2)
245                 winPoints = new POINT[npoints + 1];
246         } else
247             winPoints = new POINT[npoints];
248 
249         if (winPoints) {
250             if (transformation) {
251                 for (int i2 = 0; i2 < npoints; ++i2) {
252                     FloatPoint trPoint = transformation->mapPoint(i->at(i2));
253                     winPoints[i2].x = stableRound(trPoint.x());
254                     winPoints[i2].y = stableRound(trPoint.y());
255                 }
256             } else {
257                 for (int i2 = 0; i2 < npoints; ++i2) {
258                     winPoints[i2].x = stableRound(i->at(i2).x());
259                     winPoints[i2].y = stableRound(i->at(i2).y());
260                 }
261             }
262 
263             if (fill && winPoints[npoints - 1] != winPoints[0]) {
264                 winPoints[npoints].x = winPoints[0].x;
265                 winPoints[npoints].y = winPoints[0].y;
266                 ++npoints;
267             }
268 
269             if (fill)
270                 ::Polygon(dc, winPoints, npoints);
271             else
272                 ::Polyline(dc, winPoints, npoints);
273             delete[] winPoints;
274         }
275     }
276 }
277 
278 
numControlPoints() const279 int PlatformPathElement::numControlPoints() const
280 {
281     switch (m_type) {
282     case PathMoveTo:
283     case PathLineTo:
284         return 1;
285     case PathQuadCurveTo:
286     case PathArcTo:
287         return 2;
288     case PathBezierCurveTo:
289         return 3;
290     default:
291         ASSERT(m_type == PathCloseSubpath);
292         return 0;
293     }
294 }
295 
numPoints() const296 int PlatformPathElement::numPoints() const
297 {
298     switch (m_type) {
299     case PathMoveTo:
300     case PathLineTo:
301     case PathArcTo:
302         return 1;
303     case PathQuadCurveTo:
304         return 2;
305     case PathBezierCurveTo:
306         return 3;
307     default:
308         ASSERT(m_type == PathCloseSubpath);
309         return 0;
310     }
311 }
312 
move(const FloatSize & offset)313 void PathPolygon::move(const FloatSize& offset)
314 {
315     for (Vector<PathPoint>::iterator i = begin(); i < end(); ++i)
316         i->move(offset);
317 }
318 
transform(const AffineTransform & t)319 void PathPolygon::transform(const AffineTransform& t)
320 {
321     for (Vector<PathPoint>::iterator i = begin(); i < end(); ++i)
322         *i = t.mapPoint(*i);
323 }
324 
contains(const FloatPoint & point) const325 bool PathPolygon::contains(const FloatPoint& point) const
326 {
327     if (size() < 3)
328         return false;
329 
330     // Test intersections between the polygon and the vertical line: x = point.x()
331 
332     int intersected = 0;
333     const PathPoint* point1 = &last();
334     Vector<PathPoint>::const_iterator last = end();
335     // wasNegative: -1 means unknown, 0 means false, 1 means true.
336     int wasNegative = -1;
337     for (Vector<PathPoint>::const_iterator i = begin(); i != last; ++i) {
338         const PathPoint& point2 = *i;
339         if (point1->x() != point.x()) {
340             if (point2.x() == point.x()) {
341                 // We are getting on the vertical line
342                 wasNegative = point1->x() < point.x() ? 1 : 0;
343             } else if (point2.x() < point.x() != point1->x() < point.x()) {
344                 float y = (point2.y() - point1->y()) / (point2.x() - point1->x()) * (point.x() - point1->x()) + point1->y();
345                 if (y >= point.y())
346                     ++intersected;
347             }
348         } else {
349             // We were on the vertical line
350 
351             // handle special case
352             if (point1->y() == point.y())
353                 return true;
354 
355             if (point1->y() > point.y()) {
356                 if (point2.x() == point.x()) {
357                     // see if the point is on this segment
358                     if (point2.y() <= point.y())
359                         return true;
360 
361                     // We are still on the line
362                 } else {
363                     // We are leaving the line now.
364                     // We have to get back to see which side we come from. If we come from
365                     // the same side we are leaving, no intersection should be counted
366                     if (wasNegative < 0) {
367                         Vector<PathPoint>::const_iterator jLast = i;
368                         Vector<PathPoint>::const_iterator j = i;
369                         do {
370                             if (j == begin())
371                                 j = last;
372                             else
373                                 --j;
374                             if (j->x() != point.x()) {
375                                 if (j->x() > point.x())
376                                     wasNegative = 0;
377                                 else
378                                     wasNegative = 1;
379                                 break;
380                             }
381                         } while (j != jLast);
382 
383                         if (wasNegative < 0)
384                             return false;
385                     }
386                     if (wasNegative ? point2.x() > point.x() : point2.x() < point.x())
387                         ++intersected;
388                 }
389             } else if (point2.x() == point.x() && point2.y() >= point.y())
390                 return true;
391         }
392         point1 = &point2;
393     }
394 
395     return intersected & 1;
396 }
397 
move(const FloatSize & offset)398 void PlatformPathElement::move(const FloatSize& offset)
399 {
400     int n = numControlPoints();
401     for (int i = 0; i < n; ++i)
402         m_data.m_points[i].move(offset);
403 }
404 
transform(const AffineTransform & t)405 void PlatformPathElement::transform(const AffineTransform& t)
406 {
407     int n = numControlPoints();
408     for (int i = 0; i < n; ++i) {
409         FloatPoint p = t.mapPoint(m_data.m_points[i]);
410         m_data.m_points[i].set(p.x(), p.y());
411     }
412 }
413 
inflateRectToContainMe(FloatRect & r,const FloatPoint & lastPoint) const414 void PlatformPathElement::inflateRectToContainMe(FloatRect& r, const FloatPoint& lastPoint) const
415 {
416     if (m_type == PathArcTo) {
417         const ArcTo& data = m_data.m_arcToData;
418         PathPoint startPoint;
419         startPoint = lastPoint;
420         PathPoint endPoint = data.m_end;
421         if (!data.m_clockwise)
422             std::swap(startPoint, endPoint);
423 
424         int q0 = quadrant(startPoint, data.m_center);
425         int q1 = quadrant(endPoint, data.m_center);
426         bool containsExtremes[4] = { false }; // bottom, left, top, right
427         static const PathPoint extremeVectors[4] = { { 0, 1 }, { -1, 0 }, { 0, -1 }, { 1, 0 } };
428         if (q0 == q1) {
429             if (startPoint.m_x == endPoint.m_x || isQuadrantOnBottom(q0) != startPoint.m_x > endPoint.m_x) {
430                 for (int i = 0; i < 4; ++i)
431                     containsExtremes[i] = true;
432             }
433         } else {
434             int extreme = q0;
435             int diff = quadrantDiff(q1, q0);
436             for (int i = 0; i < diff; ++i) {
437                 containsExtremes[extreme] = true;
438                 extreme = nextQuadrant(extreme);
439             }
440         }
441 
442         inflateRectToContainPoint(r, startPoint.m_x, startPoint.m_y);
443         inflateRectToContainPoint(r, endPoint.m_x, endPoint.m_y);
444         for (int i = 0; i < 4; ++i) {
445             if (containsExtremes[i])
446                 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);
447         }
448     } else {
449         int n = numPoints();
450         for (int i = 0; i < n; ++i)
451             inflateRectToContainPoint(r, m_data.m_points[i].m_x, m_data.m_points[i].m_y);
452     }
453 }
454 
type() const455 PathElementType PlatformPathElement::type() const
456 {
457     switch (m_type) {
458     case PathMoveTo:
459         return PathElementMoveToPoint;
460     case PathLineTo:
461         return PathElementAddLineToPoint;
462     case PathArcTo:
463         // FIXME: there's no arcTo type for PathElement
464         return PathElementAddLineToPoint;
465         // return PathElementAddQuadCurveToPoint;
466     case PathQuadCurveTo:
467         return PathElementAddQuadCurveToPoint;
468     case PathBezierCurveTo:
469         return PathElementAddCurveToPoint;
470     default:
471         ASSERT(m_type == PathCloseSubpath);
472         return PathElementCloseSubpath;
473     }
474 }
475 
PlatformPath()476 PlatformPath::PlatformPath()
477     : m_penLifted(true)
478 {
479     m_currentPoint.clear();
480 }
481 
ensureSubpath()482 void PlatformPath::ensureSubpath()
483 {
484     if (m_penLifted) {
485         m_penLifted = false;
486         m_subpaths.append(PathPolygon());
487         m_subpaths.last().append(m_currentPoint);
488     } else
489         ASSERT(!m_subpaths.isEmpty());
490 }
491 
addToSubpath(const PlatformPathElement & e)492 void PlatformPath::addToSubpath(const PlatformPathElement& e)
493 {
494     if (e.platformType() == PlatformPathElement::PathMoveTo) {
495         m_penLifted = true;
496         m_currentPoint = e.pointAt(0);
497     } else if (e.platformType() == PlatformPathElement::PathCloseSubpath) {
498         m_penLifted = true;
499         if (!m_subpaths.isEmpty()) {
500             if (m_currentPoint != m_subpaths.last()[0]) {
501                 // According to W3C, we have to draw a line from current point to the initial point
502                 m_subpaths.last().append(m_subpaths.last()[0]);
503                 m_currentPoint = m_subpaths.last()[0];
504             }
505         } else
506             m_currentPoint.clear();
507     } else {
508         ensureSubpath();
509         switch (e.platformType()) {
510         case PlatformPathElement::PathLineTo:
511             m_subpaths.last().append(e.pointAt(0));
512             break;
513         case PlatformPathElement::PathArcTo:
514             addArcPoints(m_subpaths.last(), e.arcTo());
515             break;
516         case PlatformPathElement::PathQuadCurveTo:
517             {
518                 PathPoint control[] = {
519                     m_currentPoint,
520                     e.pointAt(0),
521                     e.pointAt(1),
522                 };
523                 // FIXME: magic number?
524                 quadCurve(50, m_subpaths.last(), control);
525             }
526             break;
527         case PlatformPathElement::PathBezierCurveTo:
528             {
529                 PathPoint control[] = {
530                     m_currentPoint,
531                     e.pointAt(0),
532                     e.pointAt(1),
533                     e.pointAt(2),
534                 };
535                 // FIXME: magic number?
536                 bezier(100, m_subpaths.last(), control);
537             }
538             break;
539         default:
540             ASSERT_NOT_REACHED();
541             break;
542         }
543         m_currentPoint = m_subpaths.last().last();
544     }
545 }
546 
append(const PlatformPathElement & e)547 void PlatformPath::append(const PlatformPathElement& e)
548 {
549     e.inflateRectToContainMe(m_boundingRect, lastPoint());
550     addToSubpath(e);
551     m_elements.append(e);
552 }
553 
append(const PlatformPath & p)554 void PlatformPath::append(const PlatformPath& p)
555 {
556     const PlatformPathElements& e = p.elements();
557     for (PlatformPathElements::const_iterator it(e.begin()); it != e.end(); ++it) {
558         addToSubpath(*it);
559         it->inflateRectToContainMe(m_boundingRect, lastPoint());
560         m_elements.append(*it);
561     }
562 }
563 
clear()564 void PlatformPath::clear()
565 {
566     m_elements.clear();
567     m_boundingRect = FloatRect();
568     m_subpaths.clear();
569     m_currentPoint.clear();
570     m_penLifted = true;
571 }
572 
strokePath(HDC dc,const AffineTransform * transformation) const573 void PlatformPath::strokePath(HDC dc, const AffineTransform* transformation) const
574 {
575     drawPolygons(dc, m_subpaths, false, transformation);
576 }
577 
fillPath(HDC dc,const AffineTransform * transformation) const578 void PlatformPath::fillPath(HDC dc, const AffineTransform* transformation) const
579 {
580     HGDIOBJ oldPen = SelectObject(dc, GetStockObject(NULL_PEN));
581     drawPolygons(dc, m_subpaths, true, transformation);
582     SelectObject(dc, oldPen);
583 }
584 
translate(const FloatSize & size)585 void PlatformPath::translate(const FloatSize& size)
586 {
587     for (PlatformPathElements::iterator it(m_elements.begin()); it != m_elements.end(); ++it)
588         it->move(size);
589 
590     m_boundingRect.move(size);
591     for (Vector<PathPolygon>::iterator it = m_subpaths.begin(); it != m_subpaths.end(); ++it)
592         it->move(size);
593 }
594 
transform(const AffineTransform & t)595 void PlatformPath::transform(const AffineTransform& t)
596 {
597     for (PlatformPathElements::iterator it(m_elements.begin()); it != m_elements.end(); ++it)
598         it->transform(t);
599 
600     m_boundingRect = t.mapRect(m_boundingRect);
601     for (Vector<PathPolygon>::iterator it = m_subpaths.begin(); it != m_subpaths.end(); ++it)
602         it->transform(t);
603 }
604 
contains(const FloatPoint & point,WindRule rule) const605 bool PlatformPath::contains(const FloatPoint& point, WindRule rule) const
606 {
607     // optimization: check the bounding rect first
608     if (!containsPoint(m_boundingRect, point))
609         return false;
610 
611     for (Vector<PathPolygon>::const_iterator i = m_subpaths.begin(); i != m_subpaths.end(); ++i) {
612         if (i->contains(point))
613             return true;
614     }
615 
616     return false;
617 }
618 
moveTo(const FloatPoint & point)619 void PlatformPath::moveTo(const FloatPoint& point)
620 {
621     PlatformPathElement::MoveTo data = { { point.x(), point.y() } };
622     PlatformPathElement pe(data);
623     append(pe);
624 }
625 
addLineTo(const FloatPoint & point)626 void PlatformPath::addLineTo(const FloatPoint& point)
627 {
628     PlatformPathElement::LineTo data = { { point.x(), point.y() } };
629     PlatformPathElement pe(data);
630     append(pe);
631 }
632 
addQuadCurveTo(const FloatPoint & cp,const FloatPoint & p)633 void PlatformPath::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& p)
634 {
635     PlatformPathElement::QuadCurveTo data = { { cp.x(), cp.y() }, { p.x(), p.y() } };
636     PlatformPathElement pe(data);
637     append(pe);
638 }
639 
addBezierCurveTo(const FloatPoint & cp1,const FloatPoint & cp2,const FloatPoint & p)640 void PlatformPath::addBezierCurveTo(const FloatPoint& cp1, const FloatPoint& cp2, const FloatPoint& p)
641 {
642     PlatformPathElement::BezierCurveTo data = { { cp1.x(), cp1.y() }, { cp2.x(), cp2.y() }, { p.x(), p.y() } };
643     PlatformPathElement pe(data);
644     append(pe);
645 }
646 
addArcTo(const FloatPoint & fp1,const FloatPoint & fp2,float radius)647 void PlatformPath::addArcTo(const FloatPoint& fp1, const FloatPoint& fp2, float radius)
648 {
649     const PathPoint& p0 = m_currentPoint;
650     PathPoint p1;
651     p1 = fp1;
652     PathPoint p2;
653     p2 = fp2;
654     if (!radius || p0 == p1 || p1 == p2) {
655         addLineTo(p1);
656         return;
657     }
658 
659     PathVector v01 = p0 - p1;
660     PathVector v21 = p2 - p1;
661 
662     // sin(A - B) = sin(A) * cos(B) - sin(B) * cos(A)
663     double cross = v01.m_x * v21.m_y - v01.m_y * v21.m_x;
664 
665     if (fabs(cross) < 1E-10) {
666         // on one line
667         addLineTo(p1);
668         return;
669     }
670 
671     double d01 = v01.length();
672     double d21 = v21.length();
673     double angle = (piDouble - fabs(asin(cross / (d01 * d21)))) * 0.5;
674     double span = radius * tan(angle);
675     double rate = span / d01;
676     PathPoint startPoint;
677     startPoint.m_x = p1.m_x + v01.m_x * rate;
678     startPoint.m_y = p1.m_y + v01.m_y * rate;
679 
680     addLineTo(startPoint);
681 
682     PathPoint endPoint;
683     rate = span / d21;
684     endPoint.m_x = p1.m_x + v21.m_x * rate;
685     endPoint.m_y = p1.m_y + v21.m_y * rate;
686 
687     PathPoint midPoint;
688     midPoint.m_x = (startPoint.m_x + endPoint.m_x) * 0.5;
689     midPoint.m_y = (startPoint.m_y + endPoint.m_y) * 0.5;
690 
691     PathVector vm1 = midPoint - p1;
692     double dm1 = vm1.length();
693     double d = _hypot(radius, span);
694 
695     PathPoint centerPoint;
696     rate = d / dm1;
697     centerPoint.m_x = p1.m_x + vm1.m_x * rate;
698     centerPoint.m_y = p1.m_y + vm1.m_y * rate;
699 
700     PlatformPathElement::ArcTo data = {
701         endPoint,
702         centerPoint,
703         { radius, radius },
704         cross < 0
705     };
706     PlatformPathElement pe(data);
707     append(pe);
708 }
709 
closeSubpath()710 void PlatformPath::closeSubpath()
711 {
712     PlatformPathElement pe;
713     append(pe);
714 }
715 
716 // 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)717 void PlatformPath::addEllipse(const FloatPoint& p, float a, float b, float sar, float ear, bool anticlockwise)
718 {
719     float startX, startY, endX, endY;
720 
721     normalizeAngle(sar);
722     normalizeAngle(ear);
723 
724     getEllipsePointByAngle(sar, a, b, startX, startY);
725     getEllipsePointByAngle(ear, a, b, endX, endY);
726 
727     transformArcPoint(startX, startY, p);
728     transformArcPoint(endX, endY, p);
729 
730     FloatPoint start(startX, startY);
731     moveTo(start);
732 
733     PlatformPathElement::ArcTo data = { { endX, endY }, { p.x(), p.y() },  { a, b }, !anticlockwise };
734     PlatformPathElement pe(data);
735     append(pe);
736 }
737 
738 
addRect(const FloatRect & r)739 void PlatformPath::addRect(const FloatRect& r)
740 {
741     moveTo(r.location());
742 
743     float right = r.maxX() - 1;
744     float bottom = r.maxY() - 1;
745     addLineTo(FloatPoint(right, r.y()));
746     addLineTo(FloatPoint(right, bottom));
747     addLineTo(FloatPoint(r.x(), bottom));
748     addLineTo(r.location());
749 }
750 
addEllipse(const FloatRect & r)751 void PlatformPath::addEllipse(const FloatRect& r)
752 {
753     FloatSize radius(r.width() * 0.5, r.height() * 0.5);
754     addEllipse(r.location() + radius, radius.width(), radius.height(), 0, 0, true);
755 }
756 
apply(void * info,PathApplierFunction function) const757 void PlatformPath::apply(void* info, PathApplierFunction function) const
758 {
759     PathElement pelement;
760     FloatPoint points[3];
761     pelement.points = points;
762 
763     for (PlatformPathElements::const_iterator it(m_elements.begin()); it != m_elements.end(); ++it) {
764         pelement.type = it->type();
765         int n = it->numPoints();
766         for (int i = 0; i < n; ++i)
767             points[i] = it->pointAt(i);
768         function(info, &pelement);
769     }
770 }
771 
772 } // namespace Webcore
773