2
* Copyright (C) 2007-2009 Torch Mobile, Inc.
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.
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.
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.
21
#include "PlatformPathWinCE.h"
23
#include "AffineTransform.h"
24
#include "FloatRect.h"
25
#include "GraphicsContext.h"
27
#include "WinCEGraphicsExtras.h"
28
#include <wtf/MathExtras.h>
29
#include <wtf/OwnPtr.h>
30
#include <wtf/text/WTFString.h>
36
// Implemented in GraphicsContextWinCE.cpp
37
void getEllipsePointByAngle(double angle, double a, double b, float& x, float& y);
39
static void quadCurve(int segments, Vector<PathPoint>& pts, const PathPoint* control)
41
const float step = 1.0 / segments;
42
register float tA = 0.0;
43
register float tB = 1.0;
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();
52
const int offset = pts.size();
53
pts.resize(offset + segments);
58
for (int i = 1; i < segments; ++i) {
62
const float a = tB * tB;
63
const float b = 2.0 * tA * tB;
64
const float c = tA * tA;
66
pp.m_x = c1x * a + c2x * b + c3x * c;
67
pp.m_y = c1y * a + c2y * b + c3y * c;
69
pts[offset + i - 1] = pp;
74
pts[offset + segments - 1] = pp;
77
static inline void bezier(int segments, Vector<PathPoint>& pts, const PathPoint* control)
79
const float step = 1.0 / segments;
80
register float tA = 0.0;
81
register float tB = 1.0;
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();
92
const int offset = pts.size();
93
pts.resize(offset + segments);
98
for (int i = 1; i < segments; ++i) {
101
const float tAsq = tA * tA;
102
const float tBsq = tB * tB;
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;
109
pp.m_x = c1x * a + c2x * b + c3x * c + c4x * d;
110
pp.m_y = c1y * a + c2y * b + c3y * c + c4y * d;
112
pts[offset + i - 1] = pp;
117
pts[offset + segments - 1] = pp;
120
static bool containsPoint(const FloatRect& r, const FloatPoint& p)
122
return p.x() >= r.x() && p.y() >= r.y() && p.x() < r.maxX() && p.y() < r.maxY();
125
static void normalizeAngle(float& angle)
127
angle = fmod(angle, 2 * piFloat);
129
angle += 2 * piFloat;
130
if (angle < 0.00001f)
134
static void transformArcPoint(float& x, float& y, const FloatPoint& c)
140
static void inflateRectToContainPoint(FloatRect& r, float x, float y)
145
r.setSize(FloatSize(1, 1));
149
r.setWidth(r.maxX() - x);
152
float w = x - r.x() + 1;
157
r.setHeight(r.maxY() - y);
160
float h = y - r.y() + 1;
166
// return 0-based value: 0 - first Quadrant ( 0 - 90 degree)
167
static inline int quadrant(const PathPoint& point, const PathPoint& origin)
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);
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; }
179
static inline int nextQuadrant(int q) { return q == 3 ? 0 : q + 1; }
180
static inline int quadrantDiff(int q1, int q2)
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); }
199
PathVector operator-(const PathPoint& p1, const PathPoint& p2)
201
return PathVector(p1.m_x - p2.m_x, p1.m_y - p2.m_y);
204
static void addArcPoint(PathPolygon& poly, const PathPoint& center, const PathPoint& radius, double angle)
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)
213
static void addArcPoints(PathPolygon& poly, const PlatformPathElement::ArcTo& data)
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;
223
angleStep = -angleStep;
224
if (endAngle >= curAngle || startPoint == data.m_end)
225
endAngle -= 2 * piDouble;
228
for (curAngle += angleStep; data.m_clockwise ? curAngle < endAngle : curAngle > endAngle; curAngle += angleStep)
229
addArcPoint(poly, data.m_center, data.m_radius, curAngle);
231
if (poly.isEmpty() || poly.last() != data.m_end)
232
poly.append(data.m_end);
235
static void drawPolygons(HDC dc, const Vector<PathPolygon>& polygons, bool fill, const AffineTransform* transformation)
237
for (Vector<PathPolygon>::const_iterator i = polygons.begin(); i != polygons.end(); ++i) {
238
int npoints = i->size();
242
POINT* winPoints = 0;
245
winPoints = new POINT[npoints + 1];
247
winPoints = new POINT[npoints];
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());
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());
263
if (fill && winPoints[npoints - 1] != winPoints[0]) {
264
winPoints[npoints].x = winPoints[0].x;
265
winPoints[npoints].y = winPoints[0].y;
270
::Polygon(dc, winPoints, npoints);
272
::Polyline(dc, winPoints, npoints);
279
int PlatformPathElement::numControlPoints() const
285
case PathQuadCurveTo:
288
case PathBezierCurveTo:
291
ASSERT(m_type == PathCloseSubpath);
296
int PlatformPathElement::numPoints() const
303
case PathQuadCurveTo:
305
case PathBezierCurveTo:
308
ASSERT(m_type == PathCloseSubpath);
313
void PathPolygon::move(const FloatSize& offset)
315
for (Vector<PathPoint>::iterator i = begin(); i < end(); ++i)
319
void PathPolygon::transform(const AffineTransform& t)
321
for (Vector<PathPoint>::iterator i = begin(); i < end(); ++i)
325
bool PathPolygon::contains(const FloatPoint& point) const
330
// Test intersections between the polygon and the vertical line: x = point.x()
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();
349
// We were on the vertical line
351
// handle special case
352
if (point1->y() == point.y())
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())
361
// We are still on the line
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;
374
if (j->x() != point.x()) {
375
if (j->x() > point.x())
381
} while (j != jLast);
386
if (wasNegative ? point2.x() > point.x() : point2.x() < point.x())
389
} else if (point2.x() == point.x() && point2.y() >= point.y())
395
return intersected & 1;
398
void PlatformPathElement::move(const FloatSize& offset)
400
int n = numControlPoints();
401
for (int i = 0; i < n; ++i)
402
m_data.m_points[i].move(offset);
405
void PlatformPathElement::transform(const AffineTransform& t)
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());
414
void PlatformPathElement::inflateRectToContainMe(FloatRect& r, const FloatPoint& lastPoint) const
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);
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 } };
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;
435
int diff = quadrantDiff(q1, q0);
436
for (int i = 0; i < diff; ++i) {
437
containsExtremes[extreme] = true;
438
extreme = nextQuadrant(extreme);
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);
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);
455
PathElementType PlatformPathElement::type() const
459
return PathElementMoveToPoint;
461
return PathElementAddLineToPoint;
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;
471
ASSERT(m_type == PathCloseSubpath);
472
return PathElementCloseSubpath;
476
PlatformPath::PlatformPath()
479
m_currentPoint.clear();
482
void PlatformPath::ensureSubpath()
486
m_subpaths.append(PathPolygon());
487
m_subpaths.last().append(m_currentPoint);
489
ASSERT(!m_subpaths.isEmpty());
492
void PlatformPath::addToSubpath(const PlatformPathElement& e)
494
if (e.platformType() == PlatformPathElement::PathMoveTo) {
496
m_currentPoint = e.pointAt(0);
497
} else if (e.platformType() == PlatformPathElement::PathCloseSubpath) {
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];
506
m_currentPoint.clear();
509
switch (e.platformType()) {
510
case PlatformPathElement::PathLineTo:
511
m_subpaths.last().append(e.pointAt(0));
513
case PlatformPathElement::PathArcTo:
514
addArcPoints(m_subpaths.last(), e.arcTo());
516
case PlatformPathElement::PathQuadCurveTo:
518
PathPoint control[] = {
523
// FIXME: magic number?
524
quadCurve(50, m_subpaths.last(), control);
527
case PlatformPathElement::PathBezierCurveTo:
529
PathPoint control[] = {
535
// FIXME: magic number?
536
bezier(100, m_subpaths.last(), control);
540
ASSERT_NOT_REACHED();
543
m_currentPoint = m_subpaths.last().last();
547
void PlatformPath::append(const PlatformPathElement& e)
549
e.inflateRectToContainMe(m_boundingRect, lastPoint());
551
m_elements.append(e);
554
void PlatformPath::append(const PlatformPath& p)
556
const PlatformPathElements& e = p.elements();
557
for (PlatformPathElements::const_iterator it(e.begin()); it != e.end(); ++it) {
559
it->inflateRectToContainMe(m_boundingRect, lastPoint());
560
m_elements.append(*it);
564
void PlatformPath::clear()
567
m_boundingRect = FloatRect();
569
m_currentPoint.clear();
573
void PlatformPath::strokePath(HDC dc, const AffineTransform* transformation) const
575
drawPolygons(dc, m_subpaths, false, transformation);
578
void PlatformPath::fillPath(HDC dc, const AffineTransform* transformation) const
580
HGDIOBJ oldPen = SelectObject(dc, GetStockObject(NULL_PEN));
581
drawPolygons(dc, m_subpaths, true, transformation);
582
SelectObject(dc, oldPen);
585
void PlatformPath::translate(const FloatSize& size)
587
for (PlatformPathElements::iterator it(m_elements.begin()); it != m_elements.end(); ++it)
590
m_boundingRect.move(size);
591
for (Vector<PathPolygon>::iterator it = m_subpaths.begin(); it != m_subpaths.end(); ++it)
595
void PlatformPath::transform(const AffineTransform& t)
597
for (PlatformPathElements::iterator it(m_elements.begin()); it != m_elements.end(); ++it)
600
m_boundingRect = t.mapRect(m_boundingRect);
601
for (Vector<PathPolygon>::iterator it = m_subpaths.begin(); it != m_subpaths.end(); ++it)
605
bool PlatformPath::contains(const FloatPoint& point, WindRule rule) const
607
// optimization: check the bounding rect first
608
if (!containsPoint(m_boundingRect, point))
611
for (Vector<PathPolygon>::const_iterator i = m_subpaths.begin(); i != m_subpaths.end(); ++i) {
612
if (i->contains(point))
619
void PlatformPath::moveTo(const FloatPoint& point)
621
PlatformPathElement::MoveTo data = { { point.x(), point.y() } };
622
PlatformPathElement pe(data);
626
void PlatformPath::addLineTo(const FloatPoint& point)
628
PlatformPathElement::LineTo data = { { point.x(), point.y() } };
629
PlatformPathElement pe(data);
633
void PlatformPath::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& p)
635
PlatformPathElement::QuadCurveTo data = { { cp.x(), cp.y() }, { p.x(), p.y() } };
636
PlatformPathElement pe(data);
640
void PlatformPath::addBezierCurveTo(const FloatPoint& cp1, const FloatPoint& cp2, const FloatPoint& p)
642
PlatformPathElement::BezierCurveTo data = { { cp1.x(), cp1.y() }, { cp2.x(), cp2.y() }, { p.x(), p.y() } };
643
PlatformPathElement pe(data);
647
void PlatformPath::addArcTo(const FloatPoint& fp1, const FloatPoint& fp2, float radius)
649
const PathPoint& p0 = m_currentPoint;
654
if (!radius || p0 == p1 || p1 == p2) {
659
PathVector v01 = p0 - p1;
660
PathVector v21 = p2 - p1;
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;
665
if (fabs(cross) < 1E-10) {
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;
680
addLineTo(startPoint);
684
endPoint.m_x = p1.m_x + v21.m_x * rate;
685
endPoint.m_y = p1.m_y + v21.m_y * rate;
688
midPoint.m_x = (startPoint.m_x + endPoint.m_x) * 0.5;
689
midPoint.m_y = (startPoint.m_y + endPoint.m_y) * 0.5;
691
PathVector vm1 = midPoint - p1;
692
double dm1 = vm1.length();
693
double d = _hypot(radius, span);
695
PathPoint centerPoint;
697
centerPoint.m_x = p1.m_x + vm1.m_x * rate;
698
centerPoint.m_y = p1.m_y + vm1.m_y * rate;
700
PlatformPathElement::ArcTo data = {
706
PlatformPathElement pe(data);
710
void PlatformPath::closeSubpath()
712
PlatformPathElement pe;
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)
719
float startX, startY, endX, endY;
724
getEllipsePointByAngle(sar, a, b, startX, startY);
725
getEllipsePointByAngle(ear, a, b, endX, endY);
727
transformArcPoint(startX, startY, p);
728
transformArcPoint(endX, endY, p);
730
FloatPoint start(startX, startY);
733
PlatformPathElement::ArcTo data = { { endX, endY }, { p.x(), p.y() }, { a, b }, !anticlockwise };
734
PlatformPathElement pe(data);
739
void PlatformPath::addRect(const FloatRect& r)
741
moveTo(r.location());
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());
751
void PlatformPath::addEllipse(const FloatRect& r)
753
FloatSize radius(r.width() * 0.5, r.height() * 0.5);
754
addEllipse(r.location() + radius, radius.width(), radius.height(), 0, 0, true);
757
void PlatformPath::apply(void* info, PathApplierFunction function) const
759
PathElement pelement;
760
FloatPoint points[3];
761
pelement.points = points;
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);
772
} // namespace Webcore