1/*
2 * Copyright (C) 2003, 2006 Apple Computer, Inc.  All rights reserved.
3 *                     2006, 2008 Rob Buis <buis@kde.org>
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 * 1. Redistributions of source code must retain the above copyright
9 *    notice, this list of conditions and the following disclaimer.
10 * 2. Redistributions in binary form must reproduce the above copyright
11 *    notice, this list of conditions and the following disclaimer in the
12 *    documentation and/or other materials provided with the distribution.
13 *
14 * THIS SOFTWARE IS PROVIDED BY APPLE COMPUTER, INC. ``AS IS'' AND ANY
15 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
16 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
17 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE COMPUTER, INC. OR
18 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
19 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
20 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
21 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
22 * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
23 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
24 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
25 */
26
27#include "config.h"
28#include "Path.h"
29
30#if USE(CG)
31
32#include "AffineTransform.h"
33#include "FloatRect.h"
34#include "GraphicsContext.h"
35#include "IntRect.h"
36#include "PlatformString.h"
37#include "StrokeStyleApplier.h"
38#include <ApplicationServices/ApplicationServices.h>
39#include <wtf/MathExtras.h>
40#include <wtf/RetainPtr.h>
41
42namespace WebCore {
43
44static size_t putBytesNowhere(void*, const void*, size_t count)
45{
46    return count;
47}
48
49static CGContextRef createScratchContext()
50{
51    CGDataConsumerCallbacks callbacks = { putBytesNowhere, 0 };
52    RetainPtr<CGDataConsumerRef> consumer(AdoptCF, CGDataConsumerCreate(0, &callbacks));
53    CGContextRef context = CGPDFContextCreate(consumer.get(), 0, 0);
54
55    CGFloat black[4] = { 0, 0, 0, 1 };
56    CGContextSetFillColor(context, black);
57    CGContextSetStrokeColor(context, black);
58
59    return context;
60}
61
62static inline CGContextRef scratchContext()
63{
64    static CGContextRef context = createScratchContext();
65    return context;
66}
67
68Path::Path()
69    : m_path(CGPathCreateMutable())
70{
71}
72
73Path::~Path()
74{
75    CGPathRelease(m_path);
76}
77
78Path::Path(const Path& other)
79    : m_path(CGPathCreateMutableCopy(other.m_path))
80{
81}
82
83Path& Path::operator=(const Path& other)
84{
85    CGMutablePathRef path = CGPathCreateMutableCopy(other.m_path);
86    CGPathRelease(m_path);
87    m_path = path;
88    return *this;
89}
90
91static void copyClosingSubpathsApplierFunction(void* info, const CGPathElement* element)
92{
93    CGMutablePathRef path = static_cast<CGMutablePathRef>(info);
94    CGPoint* points = element->points;
95
96    switch (element->type) {
97    case kCGPathElementMoveToPoint:
98        if (!CGPathIsEmpty(path)) // to silence a warning when trying to close an empty path
99            CGPathCloseSubpath(path); // This is the only change from CGPathCreateMutableCopy
100        CGPathMoveToPoint(path, 0, points[0].x, points[0].y);
101        break;
102    case kCGPathElementAddLineToPoint:
103        CGPathAddLineToPoint(path, 0, points[0].x, points[0].y);
104        break;
105    case kCGPathElementAddQuadCurveToPoint:
106        CGPathAddQuadCurveToPoint(path, 0, points[0].x, points[0].y, points[1].x, points[1].y);
107        break;
108    case kCGPathElementAddCurveToPoint:
109        CGPathAddCurveToPoint(path, 0, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y);
110        break;
111    case kCGPathElementCloseSubpath:
112        CGPathCloseSubpath(path);
113        break;
114    }
115}
116
117static CGMutablePathRef copyCGPathClosingSubpaths(CGPathRef originalPath)
118{
119    CGMutablePathRef path = CGPathCreateMutable();
120    CGPathApply(originalPath, path, copyClosingSubpathsApplierFunction);
121    CGPathCloseSubpath(path);
122    return path;
123}
124
125bool Path::contains(const FloatPoint &point, WindRule rule) const
126{
127    if (!boundingRect().contains(point))
128        return false;
129
130    // CGPathContainsPoint returns false for non-closed paths, as a work-around, we copy and close the path first.  Radar 4758998 asks for a better CG API to use
131    RetainPtr<CGMutablePathRef> path(AdoptCF, copyCGPathClosingSubpaths(m_path));
132    bool ret = CGPathContainsPoint(path.get(), 0, point, rule == RULE_EVENODD ? true : false);
133    return ret;
134}
135
136bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
137{
138    ASSERT(applier);
139
140    CGContextRef context = scratchContext();
141
142    CGContextSaveGState(context);
143    CGContextBeginPath(context);
144    CGContextAddPath(context, platformPath());
145
146    GraphicsContext gc(context);
147    applier->strokeStyle(&gc);
148
149    bool hitSuccess = CGContextPathContainsPoint(context, point, kCGPathStroke);
150    CGContextRestoreGState(context);
151
152    return hitSuccess;
153}
154
155void Path::translate(const FloatSize& size)
156{
157    CGAffineTransform translation = CGAffineTransformMake(1, 0, 0, 1, size.width(), size.height());
158    CGMutablePathRef newPath = CGPathCreateMutable();
159    CGPathAddPath(newPath, &translation, m_path);
160    CGPathRelease(m_path);
161    m_path = newPath;
162}
163
164FloatRect Path::boundingRect() const
165{
166    return CGPathGetBoundingBox(m_path);
167}
168
169FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) const
170{
171    CGContextRef context = scratchContext();
172
173    CGContextSaveGState(context);
174    CGContextBeginPath(context);
175    CGContextAddPath(context, platformPath());
176
177    if (applier) {
178        GraphicsContext graphicsContext(context);
179        applier->strokeStyle(&graphicsContext);
180    }
181
182    CGContextReplacePathWithStrokedPath(context);
183    CGRect box = CGContextIsPathEmpty(context) ? CGRectZero : CGContextGetPathBoundingBox(context);
184    CGContextRestoreGState(context);
185
186    return box;
187}
188
189void Path::moveTo(const FloatPoint& point)
190{
191    CGPathMoveToPoint(m_path, 0, point.x(), point.y());
192}
193
194void Path::addLineTo(const FloatPoint& p)
195{
196    CGPathAddLineToPoint(m_path, 0, p.x(), p.y());
197}
198
199void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& p)
200{
201    CGPathAddQuadCurveToPoint(m_path, 0, cp.x(), cp.y(), p.x(), p.y());
202}
203
204void Path::addBezierCurveTo(const FloatPoint& cp1, const FloatPoint& cp2, const FloatPoint& p)
205{
206    CGPathAddCurveToPoint(m_path, 0, cp1.x(), cp1.y(), cp2.x(), cp2.y(), p.x(), p.y());
207}
208
209void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
210{
211    CGPathAddArcToPoint(m_path, 0, p1.x(), p1.y(), p2.x(), p2.y(), radius);
212}
213
214void Path::closeSubpath()
215{
216    CGPathCloseSubpath(m_path);
217}
218
219void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool clockwise)
220{
221    // Workaround for <rdar://problem/5189233> CGPathAddArc hangs or crashes when passed inf as start or end angle
222    if (isfinite(sa) && isfinite(ea))
223        CGPathAddArc(m_path, 0, p.x(), p.y(), r, sa, ea, clockwise);
224}
225
226void Path::addRect(const FloatRect& r)
227{
228    CGPathAddRect(m_path, 0, r);
229}
230
231void Path::addEllipse(const FloatRect& r)
232{
233    CGPathAddEllipseInRect(m_path, 0, r);
234}
235
236void Path::clear()
237{
238    CGPathRelease(m_path);
239    m_path = CGPathCreateMutable();
240}
241
242bool Path::isEmpty() const
243{
244    return CGPathIsEmpty(m_path);
245}
246
247bool Path::hasCurrentPoint() const
248{
249    return !isEmpty();
250}
251
252FloatPoint Path::currentPoint() const
253{
254    return CGPathGetCurrentPoint(m_path);
255}
256
257// MARK: -
258// MARK: Path Management
259
260struct PathApplierInfo {
261    void* info;
262    PathApplierFunction function;
263};
264
265static void CGPathApplierToPathApplier(void *info, const CGPathElement *element)
266{
267    PathApplierInfo* pinfo = (PathApplierInfo*)info;
268    FloatPoint points[3];
269    PathElement pelement;
270    pelement.type = (PathElementType)element->type;
271    pelement.points = points;
272    CGPoint* cgPoints = element->points;
273    switch (element->type) {
274    case kCGPathElementMoveToPoint:
275    case kCGPathElementAddLineToPoint:
276        points[0] = cgPoints[0];
277        break;
278    case kCGPathElementAddQuadCurveToPoint:
279        points[0] = cgPoints[0];
280        points[1] = cgPoints[1];
281        break;
282    case kCGPathElementAddCurveToPoint:
283        points[0] = cgPoints[0];
284        points[1] = cgPoints[1];
285        points[2] = cgPoints[2];
286        break;
287    case kCGPathElementCloseSubpath:
288        break;
289    }
290    pinfo->function(pinfo->info, &pelement);
291}
292
293void Path::apply(void* info, PathApplierFunction function) const
294{
295    PathApplierInfo pinfo;
296    pinfo.info = info;
297    pinfo.function = function;
298    CGPathApply(m_path, &pinfo, CGPathApplierToPathApplier);
299}
300
301void Path::transform(const AffineTransform& transform)
302{
303    CGMutablePathRef path = CGPathCreateMutable();
304    CGAffineTransform transformCG = transform;
305    CGPathAddPath(path, &transformCG, m_path);
306    CGPathRelease(m_path);
307    m_path = path;
308}
309
310}
311
312#endif // USE(CG)
313