Part 2 of removing PlatformString.h, remove PlatformString.h
[WebKit-https.git] / Source / WebCore / platform / graphics / cg / PathCG.cpp
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 "StrokeStyleApplier.h"
37 #include <ApplicationServices/ApplicationServices.h>
38 #include <wtf/MathExtras.h>
39 #include <wtf/RetainPtr.h>
40 #include <wtf/text/WTFString.h>
41
42 #if PLATFORM(MAC) || PLATFORM(CHROMIUM)
43 #include "WebCoreSystemInterface.h"
44 #endif
45
46 #if PLATFORM(WIN)
47 #include <WebKitSystemInterface/WebKitSystemInterface.h>
48 #endif
49
50 namespace WebCore {
51
52 static size_t putBytesNowhere(void*, const void*, size_t count)
53 {
54     return count;
55 }
56
57 static 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
70 static inline CGContextRef scratchContext()
71 {
72     static CGContextRef context = createScratchContext();
73     return context;
74 }
75
76 Path::Path()
77     : m_path(0)
78 {
79 }
80
81 Path::~Path()
82 {
83     if (m_path)
84         CGPathRelease(m_path);
85 }
86
87 PlatformPathPtr Path::ensurePlatformPath()
88 {
89     if (!m_path)
90         m_path = CGPathCreateMutable();
91     return m_path;
92 }
93
94 Path::Path(const Path& other)
95 {
96     m_path = other.m_path ? CGPathCreateMutableCopy(other.m_path) : 0;
97 }
98
99 Path& Path::operator=(const Path& other)
100 {
101     CGMutablePathRef path = other.m_path ? CGPathCreateMutableCopy(other.m_path) : 0;
102     if (m_path)
103         CGPathRelease(m_path);
104     m_path = path;
105     return *this;
106 }
107
108 static void copyClosingSubpathsApplierFunction(void* info, const CGPathElement* element)
109 {
110     CGMutablePathRef path = static_cast<CGMutablePathRef>(info);
111     CGPoint* points = element->points;
112     
113     switch (element->type) {
114     case kCGPathElementMoveToPoint:
115         if (!CGPathIsEmpty(path)) // to silence a warning when trying to close an empty path
116             CGPathCloseSubpath(path); // This is the only change from CGPathCreateMutableCopy
117         CGPathMoveToPoint(path, 0, points[0].x, points[0].y);
118         break;
119     case kCGPathElementAddLineToPoint:
120         CGPathAddLineToPoint(path, 0, points[0].x, points[0].y);
121         break;
122     case kCGPathElementAddQuadCurveToPoint:
123         CGPathAddQuadCurveToPoint(path, 0, points[0].x, points[0].y, points[1].x, points[1].y);
124         break;
125     case kCGPathElementAddCurveToPoint:
126         CGPathAddCurveToPoint(path, 0, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y);
127         break;
128     case kCGPathElementCloseSubpath:
129         CGPathCloseSubpath(path);
130         break;
131     }
132 }
133
134 static CGMutablePathRef copyCGPathClosingSubpaths(CGPathRef originalPath)
135 {
136     CGMutablePathRef path = CGPathCreateMutable();
137     CGPathApply(originalPath, path, copyClosingSubpathsApplierFunction);
138     CGPathCloseSubpath(path);
139     return path;
140 }
141
142 bool Path::contains(const FloatPoint &point, WindRule rule) const
143 {
144     if (isNull())
145         return false;
146
147     if (!fastBoundingRect().contains(point))
148         return false;
149
150     // 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
151     RetainPtr<CGMutablePathRef> path(AdoptCF, copyCGPathClosingSubpaths(m_path));
152     bool ret = CGPathContainsPoint(path.get(), 0, point, rule == RULE_EVENODD ? true : false);
153     return ret;
154 }
155
156 bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
157 {
158     if (isNull())
159         return false;
160
161     ASSERT(applier);
162
163     CGContextRef context = scratchContext();
164
165     CGContextSaveGState(context);
166     CGContextBeginPath(context);
167     CGContextAddPath(context, platformPath());
168
169     GraphicsContext gc(context);
170     applier->strokeStyle(&gc);
171
172     bool hitSuccess = CGContextPathContainsPoint(context, point, kCGPathStroke);
173     CGContextRestoreGState(context);
174     
175     return hitSuccess;
176 }
177
178 void Path::translate(const FloatSize& size)
179 {
180     CGAffineTransform translation = CGAffineTransformMake(1, 0, 0, 1, size.width(), size.height());
181     CGMutablePathRef newPath = CGPathCreateMutable();
182     // FIXME: This is potentially wasteful to allocate an empty path only to create a transformed copy.
183     CGPathAddPath(newPath, &translation, ensurePlatformPath());
184     CGPathRelease(m_path);
185     m_path = newPath;
186 }
187
188 FloatRect Path::boundingRect() const
189 {
190     if (isNull())
191         return CGRectZero;
192
193     // CGPathGetBoundingBox includes the path's control points, CGPathGetPathBoundingBox
194     // does not, but only exists on 10.6 and above.
195
196     CGRect bound = CGRectZero;
197 #if !PLATFORM(MAC) || __MAC_OS_X_VERSION_MIN_REQUIRED >= 1060
198     bound = CGPathGetPathBoundingBox(m_path);
199 #else
200     bound = CGPathGetBoundingBox(m_path);
201 #endif
202     return CGRectIsNull(bound) ? CGRectZero : bound;
203 }
204
205 FloatRect Path::fastBoundingRect() const
206 {
207     if (isNull())
208         return CGRectZero;
209     CGRect bound = CGPathGetBoundingBox(m_path);
210     return CGRectIsNull(bound) ? CGRectZero : bound;
211 }
212
213 FloatRect 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
236 void Path::moveTo(const FloatPoint& point)
237 {
238     CGPathMoveToPoint(ensurePlatformPath(), 0, point.x(), point.y());
239 }
240
241 void Path::addLineTo(const FloatPoint& p)
242 {
243     CGPathAddLineToPoint(ensurePlatformPath(), 0, p.x(), p.y());
244 }
245
246 void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& p)
247 {
248     CGPathAddQuadCurveToPoint(ensurePlatformPath(), 0, cp.x(), cp.y(), p.x(), p.y());
249 }
250
251 void 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
256 void 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
261 void Path::platformAddPathForRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius)
262 {
263 #if PLATFORM(MAC) && __MAC_OS_X_VERSION_MIN_REQUIRED >= 1070
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         wkCGPathAddRoundedRect(ensurePlatformPath(), 0, rect, topLeftRadius.width(), topLeftRadius.height());
269         return;
270     }
271 #endif
272
273     addBeziersForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
274 }
275
276 void Path::closeSubpath()
277 {
278     // FIXME: Unclear if close commands should have meaning for a null path.
279     if (isNull())
280         return;
281
282     CGPathCloseSubpath(m_path);
283 }
284
285 void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool clockwise)
286 {
287     // Workaround for <rdar://problem/5189233> CGPathAddArc hangs or crashes when passed inf as start or end angle
288     if (isfinite(sa) && isfinite(ea))
289         CGPathAddArc(ensurePlatformPath(), 0, p.x(), p.y(), r, sa, ea, clockwise);
290 }
291
292 void Path::addRect(const FloatRect& r)
293 {
294     CGPathAddRect(ensurePlatformPath(), 0, r);
295 }
296
297 void Path::addEllipse(const FloatRect& r)
298 {
299     CGPathAddEllipseInRect(ensurePlatformPath(), 0, r);
300 }
301
302 void Path::clear()
303 {
304     if (isNull())
305         return;
306
307     CGPathRelease(m_path);
308     m_path = CGPathCreateMutable();
309 }
310
311 bool Path::isEmpty() const
312 {
313     return isNull() || CGPathIsEmpty(m_path);
314 }
315
316 bool Path::hasCurrentPoint() const
317 {
318     return !isEmpty();
319 }
320     
321 FloatPoint Path::currentPoint() const 
322 {
323     if (isNull())
324         return FloatPoint();
325     return CGPathGetCurrentPoint(m_path);
326 }
327
328 // MARK: -
329 // MARK: Path Management
330
331 struct PathApplierInfo {
332     void* info;
333     PathApplierFunction function;
334 };
335
336 static void CGPathApplierToPathApplier(void *info, const CGPathElement *element)
337 {
338     PathApplierInfo* pinfo = (PathApplierInfo*)info;
339     FloatPoint points[3];
340     PathElement pelement;
341     pelement.type = (PathElementType)element->type;
342     pelement.points = points;
343     CGPoint* cgPoints = element->points;
344     switch (element->type) {
345     case kCGPathElementMoveToPoint:
346     case kCGPathElementAddLineToPoint:
347         points[0] = cgPoints[0];
348         break;
349     case kCGPathElementAddQuadCurveToPoint:
350         points[0] = cgPoints[0];
351         points[1] = cgPoints[1];
352         break;
353     case kCGPathElementAddCurveToPoint:
354         points[0] = cgPoints[0];
355         points[1] = cgPoints[1];
356         points[2] = cgPoints[2];
357         break;
358     case kCGPathElementCloseSubpath:
359         break;
360     }
361     pinfo->function(pinfo->info, &pelement);
362 }
363
364 void Path::apply(void* info, PathApplierFunction function) const
365 {
366     if (isNull())
367         return;
368
369     PathApplierInfo pinfo;
370     pinfo.info = info;
371     pinfo.function = function;
372     CGPathApply(m_path, &pinfo, CGPathApplierToPathApplier);
373 }
374
375 void Path::transform(const AffineTransform& transform)
376 {
377     if (transform.isIdentity() || isEmpty())
378         return;
379
380     CGMutablePathRef path = CGPathCreateMutable();
381     CGAffineTransform transformCG = transform;
382     CGPathAddPath(path, &transformCG, m_path);
383     CGPathRelease(m_path);
384     m_path = path;
385 }
386
387 }
388
389 #endif // USE(CG)