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