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.right() && p.y() < r.bottom();
    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.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)
    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     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 
    280 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 
    297 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 
    314 void PathPolygon::move(const FloatSize& offset)
    315 {
    316     for (Vector<PathPoint>::iterator i = begin(); i < end(); ++i)
    317         i->move(offset);
    318 }
    319 
    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 
    326 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 
    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 
    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 
    415 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 
    456 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 
    477 PlatformPath::PlatformPath()
    478     : m_penLifted(true)
    479 {
    480     m_currentPoint.clear();
    481 }
    482 
    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 
    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 
    548 void PlatformPath::append(const PlatformPathElement& e)
    549 {
    550     e.inflateRectToContainMe(m_boundingRect, lastPoint());
    551     addToSubpath(e);
    552     m_elements.append(e);
    553 }
    554 
    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 
    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 
    574 void PlatformPath::strokePath(HDC dc, const AffineTransform* transformation) const
    575 {
    576     drawPolygons(dc, m_subpaths, false, transformation);
    577 }
    578 
    579 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 
    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 
    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 
    606 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 
    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 
    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 
    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 
    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 
    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 
    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
    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 
    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 
    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 
    758 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 
    795 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