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