1/*
2 * Copyright (C) 2003, 2006 Apple 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 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 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 "StrokeStyleApplier.h"
37#include <CoreGraphics/CoreGraphics.h>
38#include <wtf/MathExtras.h>
39#include <wtf/RetainPtr.h>
40#include <wtf/text/WTFString.h>
41
42#if PLATFORM(COCOA)
43#include "WebCoreSystemInterface.h"
44#endif
45
46#if PLATFORM(WIN)
47#include <WebKitSystemInterface/WebKitSystemInterface.h>
48#endif
49
50namespace WebCore {
51
52static size_t putBytesNowhere(void*, const void*, size_t count)
53{
54    return count;
55}
56
57static CGContextRef createScratchContext()
58{
59    CGDataConsumerCallbacks callbacks = { putBytesNowhere, 0 };
60    RetainPtr<CGDataConsumerRef> consumer = adoptCF(CGDataConsumerCreate(0, &callbacks));
61    CGContextRef context = CGPDFContextCreate(consumer.get(), 0, 0);
62
63    CGFloat black[4] = { 0, 0, 0, 1 };
64    CGContextSetFillColor(context, black);
65    CGContextSetStrokeColor(context, black);
66
67    return context;
68}
69
70static inline CGContextRef scratchContext()
71{
72    static CGContextRef context = createScratchContext();
73    return context;
74}
75
76Path::Path()
77    : m_path(0)
78{
79}
80
81Path::Path(RetainPtr<CGMutablePathRef> p)
82    : m_path(p.leakRef())
83{
84}
85
86Path::~Path()
87{
88    if (m_path)
89        CGPathRelease(m_path);
90}
91
92PlatformPathPtr Path::ensurePlatformPath()
93{
94    if (!m_path)
95        m_path = CGPathCreateMutable();
96    return m_path;
97}
98
99Path::Path(const Path& other)
100{
101    m_path = other.m_path ? CGPathCreateMutableCopy(other.m_path) : 0;
102}
103
104Path& Path::operator=(const Path& other)
105{
106    CGMutablePathRef path = other.m_path ? CGPathCreateMutableCopy(other.m_path) : 0;
107    if (m_path)
108        CGPathRelease(m_path);
109    m_path = path;
110    return *this;
111}
112
113static void copyClosingSubpathsApplierFunction(void* info, const CGPathElement* element)
114{
115    CGMutablePathRef path = static_cast<CGMutablePathRef>(info);
116    CGPoint* points = element->points;
117
118    switch (element->type) {
119    case kCGPathElementMoveToPoint:
120        if (!CGPathIsEmpty(path)) // to silence a warning when trying to close an empty path
121            CGPathCloseSubpath(path); // This is the only change from CGPathCreateMutableCopy
122        CGPathMoveToPoint(path, 0, points[0].x, points[0].y);
123        break;
124    case kCGPathElementAddLineToPoint:
125        CGPathAddLineToPoint(path, 0, points[0].x, points[0].y);
126        break;
127    case kCGPathElementAddQuadCurveToPoint:
128        CGPathAddQuadCurveToPoint(path, 0, points[0].x, points[0].y, points[1].x, points[1].y);
129        break;
130    case kCGPathElementAddCurveToPoint:
131        CGPathAddCurveToPoint(path, 0, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y);
132        break;
133    case kCGPathElementCloseSubpath:
134        CGPathCloseSubpath(path);
135        break;
136    }
137}
138
139static CGMutablePathRef copyCGPathClosingSubpaths(CGPathRef originalPath)
140{
141    CGMutablePathRef path = CGPathCreateMutable();
142    CGPathApply(originalPath, path, copyClosingSubpathsApplierFunction);
143    CGPathCloseSubpath(path);
144    return path;
145}
146
147bool Path::contains(const FloatPoint &point, WindRule rule) const
148{
149    if (isNull())
150        return false;
151
152    if (!fastBoundingRect().contains(point))
153        return false;
154
155    // 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
156    RetainPtr<CGMutablePathRef> path = adoptCF(copyCGPathClosingSubpaths(m_path));
157    bool ret = CGPathContainsPoint(path.get(), 0, point, rule == RULE_EVENODD ? true : false);
158    return ret;
159}
160
161bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
162{
163    if (isNull())
164        return false;
165
166    ASSERT(applier);
167
168    CGContextRef context = scratchContext();
169
170    CGContextSaveGState(context);
171    CGContextBeginPath(context);
172    CGContextAddPath(context, platformPath());
173
174    GraphicsContext gc(context);
175    applier->strokeStyle(&gc);
176
177    bool hitSuccess = CGContextPathContainsPoint(context, point, kCGPathStroke);
178    CGContextRestoreGState(context);
179
180    return hitSuccess;
181}
182
183void Path::translate(const FloatSize& size)
184{
185    CGAffineTransform translation = CGAffineTransformMake(1, 0, 0, 1, size.width(), size.height());
186    CGMutablePathRef newPath = CGPathCreateMutable();
187    // FIXME: This is potentially wasteful to allocate an empty path only to create a transformed copy.
188    CGPathAddPath(newPath, &translation, ensurePlatformPath());
189    CGPathRelease(m_path);
190    m_path = newPath;
191}
192
193FloatRect Path::boundingRect() const
194{
195    if (isNull())
196        return CGRectZero;
197
198    // CGPathGetBoundingBox includes the path's control points, CGPathGetPathBoundingBox
199    // does not, but only exists on 10.6 and above.
200
201    CGRect bound = CGPathGetPathBoundingBox(m_path);
202    return CGRectIsNull(bound) ? CGRectZero : bound;
203}
204
205FloatRect Path::fastBoundingRect() const
206{
207    if (isNull())
208        return CGRectZero;
209    CGRect bound = CGPathGetBoundingBox(m_path);
210    return CGRectIsNull(bound) ? CGRectZero : bound;
211}
212
213FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) const
214{
215    if (isNull())
216        return CGRectZero;
217
218    CGContextRef context = scratchContext();
219
220    CGContextSaveGState(context);
221    CGContextBeginPath(context);
222    CGContextAddPath(context, platformPath());
223
224    if (applier) {
225        GraphicsContext graphicsContext(context);
226        applier->strokeStyle(&graphicsContext);
227    }
228
229    CGContextReplacePathWithStrokedPath(context);
230    CGRect box = CGContextIsPathEmpty(context) ? CGRectZero : CGContextGetPathBoundingBox(context);
231    CGContextRestoreGState(context);
232
233    return CGRectIsNull(box) ? CGRectZero : box;
234}
235
236void Path::moveTo(const FloatPoint& point)
237{
238    CGPathMoveToPoint(ensurePlatformPath(), 0, point.x(), point.y());
239}
240
241void Path::addLineTo(const FloatPoint& p)
242{
243    CGPathAddLineToPoint(ensurePlatformPath(), 0, p.x(), p.y());
244}
245
246void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& p)
247{
248    CGPathAddQuadCurveToPoint(ensurePlatformPath(), 0, cp.x(), cp.y(), p.x(), p.y());
249}
250
251void Path::addBezierCurveTo(const FloatPoint& cp1, const FloatPoint& cp2, const FloatPoint& p)
252{
253    CGPathAddCurveToPoint(ensurePlatformPath(), 0, cp1.x(), cp1.y(), cp2.x(), cp2.y(), p.x(), p.y());
254}
255
256void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
257{
258    CGPathAddArcToPoint(ensurePlatformPath(), 0, p1.x(), p1.y(), p2.x(), p2.y(), radius);
259}
260
261void Path::platformAddPathForRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius)
262{
263#if PLATFORM(COCOA)
264    bool equalWidths = (topLeftRadius.width() == topRightRadius.width() && topRightRadius.width() == bottomLeftRadius.width() && bottomLeftRadius.width() == bottomRightRadius.width());
265    bool equalHeights = (topLeftRadius.height() == bottomLeftRadius.height() && bottomLeftRadius.height() == topRightRadius.height() && topRightRadius.height() == bottomRightRadius.height());
266
267    if (equalWidths && equalHeights) {
268        // Ensure that CG can render the rounded rect.
269        CGFloat radiusWidth = topLeftRadius.width();
270        CGFloat radiusHeight = topLeftRadius.height();
271        CGRect rectToDraw = rect;
272        CGFloat rectWidth = CGRectGetWidth(rectToDraw);
273        CGFloat rectHeight = CGRectGetHeight(rectToDraw);
274        if (rectWidth < 2 * radiusWidth)
275            radiusWidth = rectWidth / 2 - std::numeric_limits<CGFloat>::epsilon();
276        if (rectHeight < 2 * radiusHeight)
277            radiusHeight = rectHeight / 2 - std::numeric_limits<CGFloat>::epsilon();
278        wkCGPathAddRoundedRect(ensurePlatformPath(), 0, rectToDraw, radiusWidth, radiusHeight);
279        return;
280    }
281#endif
282
283    addBeziersForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
284}
285
286void Path::closeSubpath()
287{
288    // FIXME: Unclear if close commands should have meaning for a null path.
289    if (isNull())
290        return;
291
292    CGPathCloseSubpath(m_path);
293}
294
295void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool clockwise)
296{
297    // Workaround for <rdar://problem/5189233> CGPathAddArc hangs or crashes when passed inf as start or end angle
298    if (std::isfinite(sa) && std::isfinite(ea))
299        CGPathAddArc(ensurePlatformPath(), 0, p.x(), p.y(), r, sa, ea, clockwise);
300}
301
302void Path::addRect(const FloatRect& r)
303{
304    CGPathAddRect(ensurePlatformPath(), 0, r);
305}
306
307void Path::addEllipse(const FloatRect& r)
308{
309    CGPathAddEllipseInRect(ensurePlatformPath(), 0, r);
310}
311
312void Path::addPath(const Path& path, const AffineTransform& transform)
313{
314    if (!path.platformPath())
315        return;
316
317    if (!transform.isInvertible())
318        return;
319
320    CGAffineTransform transformCG = transform;
321    // CG doesn't allow adding a path to itself. Optimize for the common case
322    // and copy the path for the self referencing case.
323    if (ensurePlatformPath() != path.platformPath()) {
324        CGPathAddPath(ensurePlatformPath(), &transformCG, path.platformPath());
325        return;
326    }
327    CGPathRef pathCopy = CGPathCreateCopy(path.platformPath());
328    CGPathAddPath(ensurePlatformPath(), &transformCG, path.platformPath());
329    CGPathRelease(pathCopy);
330}
331
332
333void Path::clear()
334{
335    if (isNull())
336        return;
337
338    CGPathRelease(m_path);
339    m_path = CGPathCreateMutable();
340}
341
342bool Path::isEmpty() const
343{
344    return isNull() || CGPathIsEmpty(m_path);
345}
346
347bool Path::hasCurrentPoint() const
348{
349    return !isEmpty();
350}
351
352FloatPoint Path::currentPoint() const
353{
354    if (isNull())
355        return FloatPoint();
356    return CGPathGetCurrentPoint(m_path);
357}
358
359struct PathApplierInfo {
360    void* info;
361    PathApplierFunction function;
362};
363
364static void CGPathApplierToPathApplier(void *info, const CGPathElement *element)
365{
366    PathApplierInfo* pinfo = (PathApplierInfo*)info;
367    FloatPoint points[3];
368    PathElement pelement;
369    pelement.type = (PathElementType)element->type;
370    pelement.points = points;
371    CGPoint* cgPoints = element->points;
372    switch (element->type) {
373    case kCGPathElementMoveToPoint:
374    case kCGPathElementAddLineToPoint:
375        points[0] = cgPoints[0];
376        break;
377    case kCGPathElementAddQuadCurveToPoint:
378        points[0] = cgPoints[0];
379        points[1] = cgPoints[1];
380        break;
381    case kCGPathElementAddCurveToPoint:
382        points[0] = cgPoints[0];
383        points[1] = cgPoints[1];
384        points[2] = cgPoints[2];
385        break;
386    case kCGPathElementCloseSubpath:
387        break;
388    }
389    pinfo->function(pinfo->info, &pelement);
390}
391
392void Path::apply(void* info, PathApplierFunction function) const
393{
394    if (isNull())
395        return;
396
397    PathApplierInfo pinfo;
398    pinfo.info = info;
399    pinfo.function = function;
400    CGPathApply(m_path, &pinfo, CGPathApplierToPathApplier);
401}
402
403void Path::transform(const AffineTransform& transform)
404{
405    if (transform.isIdentity() || isEmpty())
406        return;
407
408    CGMutablePathRef path = CGPathCreateMutable();
409    CGAffineTransform transformCG = transform;
410    CGPathAddPath(path, &transformCG, m_path);
411    CGPathRelease(m_path);
412    m_path = path;
413}
414
415}
416
417#endif // USE(CG)
418