Make use of CG rounded-rect primitives
[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 "PlatformString.h"
37 #include "StrokeStyleApplier.h"
38 #include <ApplicationServices/ApplicationServices.h>
39 #include <wtf/MathExtras.h>
40 #include <wtf/RetainPtr.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(CGPathCreateMutable())
78 {
79 }
80
81 Path::~Path()
82 {
83     CGPathRelease(m_path);
84 }
85
86 Path::Path(const Path& other)
87     : m_path(CGPathCreateMutableCopy(other.m_path))
88 {
89 }
90
91 Path& Path::operator=(const Path& other)
92 {
93     CGMutablePathRef path = CGPathCreateMutableCopy(other.m_path);
94     CGPathRelease(m_path);
95     m_path = path;
96     return *this;
97 }
98
99 static void copyClosingSubpathsApplierFunction(void* info, const CGPathElement* element)
100 {
101     CGMutablePathRef path = static_cast<CGMutablePathRef>(info);
102     CGPoint* points = element->points;
103     
104     switch (element->type) {
105     case kCGPathElementMoveToPoint:
106         if (!CGPathIsEmpty(path)) // to silence a warning when trying to close an empty path
107             CGPathCloseSubpath(path); // This is the only change from CGPathCreateMutableCopy
108         CGPathMoveToPoint(path, 0, points[0].x, points[0].y);
109         break;
110     case kCGPathElementAddLineToPoint:
111         CGPathAddLineToPoint(path, 0, points[0].x, points[0].y);
112         break;
113     case kCGPathElementAddQuadCurveToPoint:
114         CGPathAddQuadCurveToPoint(path, 0, points[0].x, points[0].y, points[1].x, points[1].y);
115         break;
116     case kCGPathElementAddCurveToPoint:
117         CGPathAddCurveToPoint(path, 0, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y);
118         break;
119     case kCGPathElementCloseSubpath:
120         CGPathCloseSubpath(path);
121         break;
122     }
123 }
124
125 static CGMutablePathRef copyCGPathClosingSubpaths(CGPathRef originalPath)
126 {
127     CGMutablePathRef path = CGPathCreateMutable();
128     CGPathApply(originalPath, path, copyClosingSubpathsApplierFunction);
129     CGPathCloseSubpath(path);
130     return path;
131 }
132
133 bool Path::contains(const FloatPoint &point, WindRule rule) const
134 {
135     if (!fastBoundingRect().contains(point))
136         return false;
137
138     // 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
139     RetainPtr<CGMutablePathRef> path(AdoptCF, copyCGPathClosingSubpaths(m_path));
140     bool ret = CGPathContainsPoint(path.get(), 0, point, rule == RULE_EVENODD ? true : false);
141     return ret;
142 }
143
144 bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
145 {
146     ASSERT(applier);
147
148     CGContextRef context = scratchContext();
149
150     CGContextSaveGState(context);
151     CGContextBeginPath(context);
152     CGContextAddPath(context, platformPath());
153
154     GraphicsContext gc(context);
155     applier->strokeStyle(&gc);
156
157     bool hitSuccess = CGContextPathContainsPoint(context, point, kCGPathStroke);
158     CGContextRestoreGState(context);
159     
160     return hitSuccess;
161 }
162
163 void Path::translate(const FloatSize& size)
164 {
165     CGAffineTransform translation = CGAffineTransformMake(1, 0, 0, 1, size.width(), size.height());
166     CGMutablePathRef newPath = CGPathCreateMutable();
167     CGPathAddPath(newPath, &translation, m_path);
168     CGPathRelease(m_path);
169     m_path = newPath;
170 }
171
172 FloatRect Path::boundingRect() const
173 {
174     // CGPathGetBoundingBox includes the path's control points, CGPathGetPathBoundingBox
175     // does not, but only exists on 10.6 and above.
176
177     CGRect bound = CGRectZero;
178 #if !defined(BUILDING_ON_LEOPARD)
179     bound = CGPathGetPathBoundingBox(m_path);
180 #else
181     bound = CGPathGetBoundingBox(m_path);
182 #endif
183     return CGRectIsNull(bound) ? CGRectZero : bound;
184 }
185
186 FloatRect Path::fastBoundingRect() const
187 {
188     CGRect bound = CGPathGetBoundingBox(m_path);
189     return CGRectIsNull(bound) ? CGRectZero : bound;
190 }
191
192 FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier) const
193 {
194     CGContextRef context = scratchContext();
195
196     CGContextSaveGState(context);
197     CGContextBeginPath(context);
198     CGContextAddPath(context, platformPath());
199
200     if (applier) {
201         GraphicsContext graphicsContext(context);
202         applier->strokeStyle(&graphicsContext);
203     }
204
205     CGContextReplacePathWithStrokedPath(context);
206     CGRect box = CGContextIsPathEmpty(context) ? CGRectZero : CGContextGetPathBoundingBox(context);
207     CGContextRestoreGState(context);
208
209     return CGRectIsNull(box) ? CGRectZero : box;
210 }
211
212 void Path::moveTo(const FloatPoint& point)
213 {
214     CGPathMoveToPoint(m_path, 0, point.x(), point.y());
215 }
216
217 void Path::addLineTo(const FloatPoint& p)
218 {
219     CGPathAddLineToPoint(m_path, 0, p.x(), p.y());
220 }
221
222 void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& p)
223 {
224     CGPathAddQuadCurveToPoint(m_path, 0, cp.x(), cp.y(), p.x(), p.y());
225 }
226
227 void Path::addBezierCurveTo(const FloatPoint& cp1, const FloatPoint& cp2, const FloatPoint& p)
228 {
229     CGPathAddCurveToPoint(m_path, 0, cp1.x(), cp1.y(), cp2.x(), cp2.y(), p.x(), p.y());
230 }
231
232 void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
233 {
234     CGPathAddArcToPoint(m_path, 0, p1.x(), p1.y(), p2.x(), p2.y(), radius);
235 }
236
237 void Path::addPathForRoundedRect(const FloatRect& rect, const FloatSize& topLeftRadius, const FloatSize& topRightRadius, const FloatSize& bottomLeftRadius, const FloatSize& bottomRightRadius)
238 {
239 #if !defined(BUILDING_ON_LEOPARD) && !defined(BUILDING_ON_SNOW_LEOPARD)
240     bool equalWidths = (topLeftRadius.width() == topRightRadius.width() && topRightRadius.width() == bottomLeftRadius.width() && bottomLeftRadius.width() == bottomRightRadius.width());
241     bool equalHeights = (topLeftRadius.height() == bottomLeftRadius.height() && bottomLeftRadius.height() == topRightRadius.height() && topRightRadius.height() == bottomRightRadius.height());
242
243     if (equalWidths && equalHeights) {
244         wkCGPathAddRoundedRect(m_path, 0, rect, topLeftRadius.width(), topLeftRadius.height());
245         return;
246     }
247 #endif
248
249     addBeziersForRoundedRect(rect, topLeftRadius, topRightRadius, bottomLeftRadius, bottomRightRadius);
250 }
251
252 void Path::closeSubpath()
253 {
254     CGPathCloseSubpath(m_path);
255 }
256
257 void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool clockwise)
258 {
259     // Workaround for <rdar://problem/5189233> CGPathAddArc hangs or crashes when passed inf as start or end angle
260     if (isfinite(sa) && isfinite(ea))
261         CGPathAddArc(m_path, 0, p.x(), p.y(), r, sa, ea, clockwise);
262 }
263
264 void Path::addRect(const FloatRect& r)
265 {
266     CGPathAddRect(m_path, 0, r);
267 }
268
269 void Path::addEllipse(const FloatRect& r)
270 {
271     CGPathAddEllipseInRect(m_path, 0, r);
272 }
273
274 void Path::clear()
275 {
276     CGPathRelease(m_path);
277     m_path = CGPathCreateMutable();
278 }
279
280 bool Path::isEmpty() const
281 {
282     return CGPathIsEmpty(m_path);
283 }
284
285 bool Path::hasCurrentPoint() const
286 {
287     return !isEmpty();
288 }
289     
290 FloatPoint Path::currentPoint() const 
291 {
292     return CGPathGetCurrentPoint(m_path);
293 }
294
295 // MARK: -
296 // MARK: Path Management
297
298 struct PathApplierInfo {
299     void* info;
300     PathApplierFunction function;
301 };
302
303 static void CGPathApplierToPathApplier(void *info, const CGPathElement *element)
304 {
305     PathApplierInfo* pinfo = (PathApplierInfo*)info;
306     FloatPoint points[3];
307     PathElement pelement;
308     pelement.type = (PathElementType)element->type;
309     pelement.points = points;
310     CGPoint* cgPoints = element->points;
311     switch (element->type) {
312     case kCGPathElementMoveToPoint:
313     case kCGPathElementAddLineToPoint:
314         points[0] = cgPoints[0];
315         break;
316     case kCGPathElementAddQuadCurveToPoint:
317         points[0] = cgPoints[0];
318         points[1] = cgPoints[1];
319         break;
320     case kCGPathElementAddCurveToPoint:
321         points[0] = cgPoints[0];
322         points[1] = cgPoints[1];
323         points[2] = cgPoints[2];
324         break;
325     case kCGPathElementCloseSubpath:
326         break;
327     }
328     pinfo->function(pinfo->info, &pelement);
329 }
330
331 void Path::apply(void* info, PathApplierFunction function) const
332 {
333     PathApplierInfo pinfo;
334     pinfo.info = info;
335     pinfo.function = function;
336     CGPathApply(m_path, &pinfo, CGPathApplierToPathApplier);
337 }
338
339 void Path::transform(const AffineTransform& transform)
340 {
341     if (transform.isIdentity() || isEmpty())
342         return;
343
344     CGMutablePathRef path = CGPathCreateMutable();
345     CGAffineTransform transformCG = transform;
346     CGPathAddPath(path, &transformCG, m_path);
347     CGPathRelease(m_path);
348     m_path = path;
349 }
350
351 }
352
353 #endif // USE(CG)