1/*
2 * Copyright 2007, The Android Open Source Project
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 *  * Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 *  * Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS ``AS IS'' AND ANY
14 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT OWNER OR
17 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "Path.h"
28
29#include "AffineTransform.h"
30#include "FloatRect.h"
31#include "GraphicsContext.h"
32#include "ImageBuffer.h"
33#include "PlatformGraphicsContext.h"
34#include "SkiaUtils.h"
35#include "SkPaint.h"
36#include "SkPath.h"
37#include "SkRegion.h"
38#include "StrokeStyleApplier.h"
39#include "TransformationMatrix.h"
40#include "android_graphics.h"
41
42namespace WebCore {
43
44Path::Path()
45{
46    m_path = new SkPath;
47//    m_path->setFlags(SkPath::kWinding_FillType);
48}
49
50Path::Path(const Path& other)
51{
52    m_path = new SkPath(*other.m_path);
53}
54
55Path::~Path()
56{
57    delete m_path;
58}
59
60Path& Path::operator=(const Path& other)
61{
62    *m_path = *other.m_path;
63    return *this;
64}
65
66bool Path::isEmpty() const
67{
68    return m_path->isEmpty();
69}
70
71bool Path::hasCurrentPoint() const
72{
73    // webkit wants to know if we have any points, including any moveTos.
74    // Skia's empty() will return true if it has just a moveTo, so we need to
75    // call getPoints(NULL), which returns the number of points,
76    // including moveTo.
77    return m_path->getPoints(0, 0) > 0;
78}
79
80FloatPoint Path::currentPoint() const
81{
82    if (hasCurrentPoint()) {
83        SkPoint lastPt;
84        m_path->getLastPt(&lastPt);
85        return lastPt;
86    }
87    float quietNaN = std::numeric_limits<float>::quiet_NaN();
88    return FloatPoint(quietNaN, quietNaN);
89}
90
91bool Path::contains(const FloatPoint& point, WindRule rule) const
92{
93    SkRegion    rgn, clip;
94
95    int x = (int)floorf(point.x());
96    int y = (int)floorf(point.y());
97    clip.setRect(x, y, x + 1, y + 1);
98
99    SkPath::FillType ft = m_path->getFillType();    // save
100    m_path->setFillType(rule == RULE_NONZERO ? SkPath::kWinding_FillType : SkPath::kEvenOdd_FillType);
101
102    bool contains = rgn.setPath(*m_path, clip);
103
104    m_path->setFillType(ft);    // restore
105    return contains;
106}
107
108void Path::translate(const FloatSize& size)
109{
110    m_path->offset(SkFloatToScalar(size.width()), SkFloatToScalar(size.height()));
111}
112
113FloatRect Path::boundingRect() const
114{
115    const SkRect& r = m_path->getBounds();
116    return FloatRect(   SkScalarToFloat(r.fLeft),
117                        SkScalarToFloat(r.fTop),
118                        SkScalarToFloat(r.width()),
119                        SkScalarToFloat(r.height()));
120}
121
122void Path::moveTo(const FloatPoint& point)
123{
124    m_path->moveTo(SkFloatToScalar(point.x()), SkFloatToScalar(point.y()));
125}
126
127void Path::addLineTo(const FloatPoint& p)
128{
129    m_path->lineTo(SkFloatToScalar(p.x()), SkFloatToScalar(p.y()));
130}
131
132void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& ep)
133{
134    m_path->quadTo( SkFloatToScalar(cp.x()), SkFloatToScalar(cp.y()),
135                    SkFloatToScalar(ep.x()), SkFloatToScalar(ep.y()));
136}
137
138void Path::addBezierCurveTo(const FloatPoint& p1, const FloatPoint& p2, const FloatPoint& ep)
139{
140    m_path->cubicTo(SkFloatToScalar(p1.x()), SkFloatToScalar(p1.y()),
141                    SkFloatToScalar(p2.x()), SkFloatToScalar(p2.y()),
142                    SkFloatToScalar(ep.x()), SkFloatToScalar(ep.y()));
143}
144
145void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
146{
147    m_path->arcTo(SkFloatToScalar(p1.x()), SkFloatToScalar(p1.y()),
148                  SkFloatToScalar(p2.x()), SkFloatToScalar(p2.y()),
149                  SkFloatToScalar(radius));
150}
151
152void Path::closeSubpath()
153{
154    m_path->close();
155}
156
157static const float gPI  = 3.14159265f;
158static const float g2PI = 6.28318531f;
159static const float g180OverPI = 57.29577951308f;
160
161static float fast_mod(float angle, float max) {
162    if (angle >= max || angle <= -max) {
163        angle = fmodf(angle, max);
164    }
165    return angle;
166}
167
168void Path::addArc(const FloatPoint& p, float r, float sa, float ea,
169                  bool clockwise) {
170    SkScalar    cx = SkFloatToScalar(p.x());
171    SkScalar    cy = SkFloatToScalar(p.y());
172    SkScalar    radius = SkFloatToScalar(r);
173
174    SkRect  oval;
175    oval.set(cx - radius, cy - radius, cx + radius, cy + radius);
176
177    float sweep = ea - sa;
178    bool prependOval = false;
179
180    /*  Note if clockwise and the sign of the sweep disagree. This particular
181        logic was deduced from http://canvex.lazyilluminati.com/misc/arc.html
182    */
183    if (clockwise && (sweep > 0 || sweep < -g2PI)) {
184        sweep = fmodf(sweep, g2PI) - g2PI;
185    } else if (!clockwise && (sweep < 0 || sweep > g2PI)) {
186        sweep = fmodf(sweep, g2PI) + g2PI;
187    }
188
189    // If the abs(sweep) >= 2PI, then we need to add a circle before we call
190    // arcTo, since it treats the sweep mod 2PI. We don't have a prepend call,
191    // so we just remember this, and at the end create a new path with an oval
192    // and our current path, and then swap then.
193    //
194    if (sweep >= g2PI || sweep <= -g2PI) {
195        prependOval = true;
196//        SkDebugf("addArc sa=%g ea=%g cw=%d sweep %g treat as circle\n", sa, ea, clockwise, sweep);
197
198        // now reduce sweep to just the amount we need, so that the current
199        // point is left where the caller expects it.
200        sweep = fmodf(sweep, g2PI);
201    }
202
203    sa = fast_mod(sa, g2PI);
204    SkScalar startDegrees = SkFloatToScalar(sa * g180OverPI);
205    SkScalar sweepDegrees = SkFloatToScalar(sweep * g180OverPI);
206
207//    SkDebugf("addArc sa=%g ea=%g cw=%d sweep=%g ssweep=%g\n", sa, ea, clockwise, sweep, SkScalarToFloat(sweepDegrees));
208    m_path->arcTo(oval, startDegrees, sweepDegrees, false);
209
210    if (prependOval) {
211        SkPath tmp;
212        tmp.addOval(oval);
213        tmp.addPath(*m_path);
214        m_path->swap(tmp);
215    }
216}
217
218void Path::addRect(const FloatRect& rect)
219{
220    m_path->addRect(rect);
221}
222
223void Path::addEllipse(const FloatRect& rect)
224{
225    m_path->addOval(rect);
226}
227
228void Path::clear()
229{
230    m_path->reset();
231}
232
233static FloatPoint* setfpts(FloatPoint dst[], const SkPoint src[], int count)
234{
235    for (int i = 0; i < count; i++)
236    {
237        dst[i].setX(SkScalarToFloat(src[i].fX));
238        dst[i].setY(SkScalarToFloat(src[i].fY));
239    }
240    return dst;
241}
242
243void Path::apply(void* info, PathApplierFunction function) const
244{
245    SkPath::Iter    iter(*m_path, false);
246    SkPoint         pts[4];
247
248    PathElement     elem;
249    FloatPoint      fpts[3];
250
251    for (;;)
252    {
253        switch (iter.next(pts)) {
254        case SkPath::kMove_Verb:
255            elem.type = PathElementMoveToPoint;
256            elem.points = setfpts(fpts, &pts[0], 1);
257            break;
258        case SkPath::kLine_Verb:
259            elem.type = PathElementAddLineToPoint;
260            elem.points = setfpts(fpts, &pts[1], 1);
261            break;
262        case SkPath::kQuad_Verb:
263            elem.type = PathElementAddQuadCurveToPoint;
264            elem.points = setfpts(fpts, &pts[1], 2);
265            break;
266        case SkPath::kCubic_Verb:
267            elem.type = PathElementAddCurveToPoint;
268            elem.points = setfpts(fpts, &pts[1], 3);
269            break;
270        case SkPath::kClose_Verb:
271            elem.type = PathElementCloseSubpath;
272            elem.points = setfpts(fpts, 0, 0);
273            break;
274        case SkPath::kDone_Verb:
275            return;
276        }
277        function(info, &elem);
278    }
279}
280
281void Path::transform(const AffineTransform& xform)
282{
283    m_path->transform(xform);
284}
285
286///////////////////////////////////////////////////////////////////////////////
287
288FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) const
289{
290    GraphicsContext* scratch = scratchContext();
291    scratch->save();
292
293    if (applier)
294        applier->strokeStyle(scratch);
295
296    SkPaint paint;
297    scratch->setupStrokePaint(&paint);
298    SkPath boundingPath;
299    paint.getFillPath(*platformPath(), &boundingPath);
300
301    FloatRect r = boundingPath.getBounds();
302    scratch->restore();
303    return r;
304}
305
306#if ENABLE(SVG)
307bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
308{
309    GraphicsContext* scratch = scratchContext();
310    scratch->save();
311
312    applier->strokeStyle(scratch);
313
314    SkPaint paint;
315    scratch->setupStrokePaint(&paint);
316    SkPath strokePath;
317    paint.getFillPath(*platformPath(), &strokePath);
318    bool contains = SkPathContainsPoint(&strokePath, point,
319                                        SkPath::kWinding_FillType);
320
321    scratch->restore();
322    return contains;
323}
324#endif
325
326}
327