• 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.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