2009-07-14 Dmitry Titov <dimich@chromium.org>
[WebKit-https.git] / 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 PLATFORM(CG)
31
32 #include "TransformationMatrix.h"
33 #include <ApplicationServices/ApplicationServices.h>
34 #include "FloatRect.h"
35 #include "GraphicsContext.h"
36 #include "IntRect.h"
37 #include "PlatformString.h"
38 #include "StrokeStyleApplier.h"
39
40 #include <wtf/MathExtras.h>
41
42 namespace WebCore {
43
44 static size_t putBytesNowhere(void*, const void*, size_t count)
45 {
46     return count;
47 }
48
49 static CGContextRef createScratchContext()
50 {
51     CGDataConsumerCallbacks callbacks = { putBytesNowhere, 0 };
52     CGDataConsumerRef consumer = CGDataConsumerCreate(0, &callbacks);
53     CGContextRef context = CGPDFContextCreate(consumer, 0, 0);
54     CGDataConsumerRelease(consumer);
55
56     CGFloat black[4] = { 0, 0, 0, 1 };
57     CGContextSetFillColor(context, black);
58     CGContextSetStrokeColor(context, black);
59
60     return context;
61 }
62
63 static inline CGContextRef scratchContext()
64 {
65     static CGContextRef context = createScratchContext();
66     return context;
67 }
68
69 Path::Path()
70     : m_path(CGPathCreateMutable())
71 {
72 }
73
74 Path::~Path()
75 {
76     CGPathRelease(m_path);
77 }
78
79 Path::Path(const Path& other)
80     : m_path(CGPathCreateMutableCopy(other.m_path))
81 {
82 }
83
84 Path& Path::operator=(const Path& other)
85 {
86     CGMutablePathRef path = CGPathCreateMutableCopy(other.m_path);
87     CGPathRelease(m_path);
88     m_path = path;
89     return *this;
90 }
91
92 static void copyClosingSubpathsApplierFunction(void* info, const CGPathElement* element)
93 {
94     CGMutablePathRef path = static_cast<CGMutablePathRef>(info);
95     CGPoint* points = element->points;
96     
97     switch (element->type) {
98     case kCGPathElementMoveToPoint:
99         if (!CGPathIsEmpty(path)) // to silence a warning when trying to close an empty path
100             CGPathCloseSubpath(path); // This is the only change from CGPathCreateMutableCopy
101         CGPathMoveToPoint(path, 0, points[0].x, points[0].y);
102         break;
103     case kCGPathElementAddLineToPoint:
104         CGPathAddLineToPoint(path, 0, points[0].x, points[0].y);
105         break;
106     case kCGPathElementAddQuadCurveToPoint:
107         CGPathAddQuadCurveToPoint(path, 0, points[0].x, points[0].y, points[1].x, points[1].y);
108         break;
109     case kCGPathElementAddCurveToPoint:
110         CGPathAddCurveToPoint(path, 0, points[0].x, points[0].y, points[1].x, points[1].y, points[2].x, points[2].y);
111         break;
112     case kCGPathElementCloseSubpath:
113         CGPathCloseSubpath(path);
114         break;
115     }
116 }
117
118 static CGMutablePathRef copyCGPathClosingSubpaths(CGPathRef originalPath)
119 {
120     CGMutablePathRef path = CGPathCreateMutable();
121     CGPathApply(originalPath, path, copyClosingSubpathsApplierFunction);
122     CGPathCloseSubpath(path);
123     return path;
124 }
125
126 bool Path::contains(const FloatPoint &point, WindRule rule) const
127 {
128     if (!boundingRect().contains(point))
129         return false;
130
131     // 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
132     CGMutablePathRef path = copyCGPathClosingSubpaths(m_path);
133     bool ret = CGPathContainsPoint(path, 0, point, rule == RULE_EVENODD ? true : false);
134     CGPathRelease(path);
135     return ret;
136 }
137
138 bool Path::strokeContains(StrokeStyleApplier* applier, const FloatPoint& point) const
139 {
140     ASSERT(applier);
141
142     CGContextRef context = scratchContext();
143
144     CGContextSaveGState(context);
145     CGContextBeginPath(context);
146     CGContextAddPath(context, platformPath());
147
148     GraphicsContext gc(context);
149     applier->strokeStyle(&gc);
150
151     bool hitSuccess = CGContextPathContainsPoint(context, point, kCGPathStroke);
152     CGContextRestoreGState(context);
153     
154     return hitSuccess;
155 }
156
157 void Path::translate(const FloatSize& size)
158 {
159     CGAffineTransform translation = CGAffineTransformMake(1, 0, 0, 1, size.width(), size.height());
160     CGMutablePathRef newPath = CGPathCreateMutable();
161     CGPathAddPath(newPath, &translation, m_path);
162     CGPathRelease(m_path);
163     m_path = newPath;
164 }
165
166 FloatRect Path::boundingRect() const
167 {
168     return CGPathGetBoundingBox(m_path);
169 }
170
171 FloatRect Path::strokeBoundingRect(StrokeStyleApplier* applier)
172 {
173     CGContextRef context = scratchContext();
174
175     CGContextSaveGState(context);
176     CGContextBeginPath(context);
177     CGContextAddPath(context, platformPath());
178
179     if (applier) {
180         GraphicsContext graphicsContext(context);
181         applier->strokeStyle(&graphicsContext);
182     }
183
184     CGContextReplacePathWithStrokedPath(context);
185     CGRect box = CGContextIsPathEmpty(context) ? CGRectZero : CGContextGetPathBoundingBox(context);
186     CGContextRestoreGState(context);
187
188     return box;
189 }
190
191 void Path::moveTo(const FloatPoint& point)
192 {
193     CGPathMoveToPoint(m_path, 0, point.x(), point.y());
194 }
195
196 void Path::addLineTo(const FloatPoint& p)
197 {
198     CGPathAddLineToPoint(m_path, 0, p.x(), p.y());
199 }
200
201 void Path::addQuadCurveTo(const FloatPoint& cp, const FloatPoint& p)
202 {
203     CGPathAddQuadCurveToPoint(m_path, 0, cp.x(), cp.y(), p.x(), p.y());
204 }
205
206 void Path::addBezierCurveTo(const FloatPoint& cp1, const FloatPoint& cp2, const FloatPoint& p)
207 {
208     CGPathAddCurveToPoint(m_path, 0, cp1.x(), cp1.y(), cp2.x(), cp2.y(), p.x(), p.y());
209 }
210
211 void Path::addArcTo(const FloatPoint& p1, const FloatPoint& p2, float radius)
212 {
213     CGPathAddArcToPoint(m_path, 0, p1.x(), p1.y(), p2.x(), p2.y(), radius);
214 }
215
216 void Path::closeSubpath()
217 {
218     if (!CGPathIsEmpty(m_path)) // to silence a warning when trying to close an empty path
219         CGPathCloseSubpath(m_path);
220 }
221
222 void Path::addArc(const FloatPoint& p, float r, float sa, float ea, bool clockwise)
223 {
224     // Workaround for <rdar://problem/5189233> CGPathAddArc hangs or crashes when passed inf as start or end angle
225     if (isfinite(sa) && isfinite(ea))
226         CGPathAddArc(m_path, 0, p.x(), p.y(), r, sa, ea, clockwise);
227 }
228
229 void Path::addRect(const FloatRect& r)
230 {
231     CGPathAddRect(m_path, 0, r);
232 }
233
234 void Path::addEllipse(const FloatRect& r)
235 {
236     CGPathAddEllipseInRect(m_path, 0, r);
237 }
238
239 void Path::clear()
240 {
241     CGPathRelease(m_path);
242     m_path = CGPathCreateMutable();
243 }
244
245 bool Path::isEmpty() const
246 {
247     return CGPathIsEmpty(m_path);
248 }
249
250 bool Path::hasCurrentPoint() const
251 {
252     return !isEmpty();
253 }
254
255 static void CGPathToCFStringApplierFunction(void* info, const CGPathElement *element)
256 {
257     CFMutableStringRef string = static_cast<CFMutableStringRef>(info);
258
259     CGPoint* points = element->points;
260     switch (element->type) {
261     case kCGPathElementMoveToPoint:
262         CFStringAppendFormat(string, 0, CFSTR("M%.2f,%.2f "), points[0].x, points[0].y);
263         break;
264     case kCGPathElementAddLineToPoint:
265         CFStringAppendFormat(string, 0, CFSTR("L%.2f,%.2f "), points[0].x, points[0].y);
266         break;
267     case kCGPathElementAddQuadCurveToPoint:
268         CFStringAppendFormat(string, 0, CFSTR("Q%.2f,%.2f,%.2f,%.2f "),
269                 points[0].x, points[0].y, points[1].x, points[1].y);
270         break;
271     case kCGPathElementAddCurveToPoint:
272         CFStringAppendFormat(string, 0, CFSTR("C%.2f,%.2f,%.2f,%.2f,%.2f,%.2f "),
273                 points[0].x, points[0].y, points[1].x, points[1].y,
274                 points[2].x, points[2].y);
275         break;
276     case kCGPathElementCloseSubpath:
277         CFStringAppendFormat(string, 0, CFSTR("Z "));
278         break;
279     }
280 }
281
282 static CFStringRef CFStringFromCGPath(CGPathRef path)
283 {
284     if (!path)
285         return 0;
286
287     CFMutableStringRef string = CFStringCreateMutable(NULL, 0);
288     CGPathApply(path, string, CGPathToCFStringApplierFunction);
289     CFStringTrimWhitespace(string);
290
291
292     return string;
293 }
294
295
296 #pragma mark -
297 #pragma mark Path Management
298
299 String Path::debugString() const
300 {
301     String result;
302     if (!isEmpty()) {
303         CFStringRef pathString = CFStringFromCGPath(m_path);
304         result = String(pathString);
305         CFRelease(pathString);
306     }
307     return result;
308 }
309
310 struct PathApplierInfo {
311     void* info;
312     PathApplierFunction function;
313 };
314
315 static void CGPathApplierToPathApplier(void *info, const CGPathElement *element)
316 {
317     PathApplierInfo* pinfo = (PathApplierInfo*)info;
318     FloatPoint points[3];
319     PathElement pelement;
320     pelement.type = (PathElementType)element->type;
321     pelement.points = points;
322     CGPoint* cgPoints = element->points;
323     switch (element->type) {
324     case kCGPathElementMoveToPoint:
325     case kCGPathElementAddLineToPoint:
326         points[0] = cgPoints[0];
327         break;
328     case kCGPathElementAddQuadCurveToPoint:
329         points[0] = cgPoints[0];
330         points[1] = cgPoints[1];
331         break;
332     case kCGPathElementAddCurveToPoint:
333         points[0] = cgPoints[0];
334         points[1] = cgPoints[1];
335         points[2] = cgPoints[2];
336         break;
337     case kCGPathElementCloseSubpath:
338         break;
339     }
340     pinfo->function(pinfo->info, &pelement);
341 }
342
343 void Path::apply(void* info, PathApplierFunction function) const
344 {
345     PathApplierInfo pinfo;
346     pinfo.info = info;
347     pinfo.function = function;
348     CGPathApply(m_path, &pinfo, CGPathApplierToPathApplier);
349 }
350
351 void Path::transform(const TransformationMatrix& transform)
352 {
353     CGMutablePathRef path = CGPathCreateMutable();
354     CGAffineTransform transformCG = transform;
355     CGPathAddPath(path, &transformCG, m_path);
356     CGPathRelease(m_path);
357     m_path = path;
358 }
359
360 }
361
362 #endif // PLATFORM(CG)