Home | History | Annotate | Download | only in wince
      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 
     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 
     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 
    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 
    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 
    134 static void transformArcPoint(float& x, float& y, const FloatPoint& c)
    135 {
    136     x += c.x();
    137     y += c.y();
    138 }
    139 
    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)
    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 
    174 static inline bool isQuadrantOnLeft(int q) { return q == 1 || q == 2; }
    175 static inline bool isQuadrantOnRight(int q) { return q == 0 || q == 3; }
    176 static inline bool isQuadrantOnTop(int q) { return q == 2 || q == 3; }
    177 static inline bool isQuadrantOnBottom(int q) { return q == 0 || q == 1; }
    178 
    179 static inline int nextQuadrant(int q) { return q == 3 ? 0 : q + 1; }
    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 
    192     PathVector() : m_x(0), m_y(0) {}
    193     PathVector(float x, float y) : m_x(x), m_y(y) {}
    194     double angle() const { return atan2(m_y, m_x); }
    195     operator double () const { return angle(); }
    196     double length() const { return _hypot(m_x, m_y); }
    197 };
    198 
    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 
    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 
    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 
    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 
    279 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 
    296 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 
    313 void PathPolygon::move(const FloatSize& offset)
    314 {
    315     for (Vector<PathPoint>::iterator i = begin(); i < end(); ++i)
    316         i->move(offset);
    317 }
    318 
    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 
    325 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 
    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 
    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 
    414 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 
    455 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 
    476 PlatformPath::PlatformPath()
    477     : m_penLifted(true)
    478 {
    479     m_currentPoint.clear();
    480 }
    481 
    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 
    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 
    547 void PlatformPath::append(const PlatformPathElement& e)
    548 {
    549     e.inflateRectToContainMe(m_boundingRect, lastPoint());
    550     addToSubpath(e);
    551     m_elements.append(e);
    552 }
    553 
    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 
    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 
    573 void PlatformPath::strokePath(HDC dc, const AffineTransform* transformation) const
    574 {
    575     drawPolygons(dc, m_subpaths, false, transformation);
    576 }
    577 
    578 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 
    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 
    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 
    605 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 
    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 
    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 
    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 
    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 
    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 
    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
    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 
    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 
    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 
    757 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