Merge b2g-inbound to m-c. a=merge
[gecko.git] / gfx / 2d / PathCG.cpp
blobd40f84f9aafe1456c6050925d9b65c19a4539af6
1 /* -*- Mode: C++; tab-width: 20; indent-tabs-mode: nil; c-basic-offset: 2 -*-
2 * This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #include "PathCG.h"
7 #include <math.h>
8 #include "DrawTargetCG.h"
9 #include "Logging.h"
11 namespace mozilla {
12 namespace gfx {
14 PathBuilderCG::~PathBuilderCG()
16 CGPathRelease(mCGPath);
19 void
20 PathBuilderCG::MoveTo(const Point &aPoint)
22 CGPathMoveToPoint(mCGPath, nullptr, aPoint.x, aPoint.y);
25 void
26 PathBuilderCG::LineTo(const Point &aPoint)
28 if (CGPathIsEmpty(mCGPath))
29 MoveTo(aPoint);
30 else
31 CGPathAddLineToPoint(mCGPath, nullptr, aPoint.x, aPoint.y);
34 void
35 PathBuilderCG::BezierTo(const Point &aCP1,
36 const Point &aCP2,
37 const Point &aCP3)
40 if (CGPathIsEmpty(mCGPath))
41 MoveTo(aCP1);
42 CGPathAddCurveToPoint(mCGPath, nullptr,
43 aCP1.x, aCP1.y,
44 aCP2.x, aCP2.y,
45 aCP3.x, aCP3.y);
49 void
50 PathBuilderCG::QuadraticBezierTo(const Point &aCP1,
51 const Point &aCP2)
53 if (CGPathIsEmpty(mCGPath))
54 MoveTo(aCP1);
55 CGPathAddQuadCurveToPoint(mCGPath, nullptr,
56 aCP1.x, aCP1.y,
57 aCP2.x, aCP2.y);
60 void
61 PathBuilderCG::Close()
63 if (!CGPathIsEmpty(mCGPath))
64 CGPathCloseSubpath(mCGPath);
67 void
68 PathBuilderCG::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
69 Float aEndAngle, bool aAntiClockwise)
71 // Core Graphic's initial coordinate system is y-axis up, whereas Moz2D's is
72 // y-axis down. Core Graphics therefore considers "clockwise" to mean "sweep
73 // in the direction of decreasing angle" whereas Moz2D considers it to mean
74 // "sweep in the direction of increasing angle". In other words if this
75 // Moz2D method is instructed to sweep anti-clockwise we need to tell
76 // CGPathAddArc to sweep clockwise, and vice versa. Hence why we pass the
77 // value of aAntiClockwise directly to CGPathAddArc's "clockwise" bool
78 // parameter.
79 CGPathAddArc(mCGPath, nullptr,
80 aOrigin.x, aOrigin.y,
81 aRadius,
82 aStartAngle,
83 aEndAngle,
84 aAntiClockwise);
87 Point
88 PathBuilderCG::CurrentPoint() const
90 CGPoint pt = CGPathGetCurrentPoint(mCGPath);
91 Point ret(pt.x, pt.y);
92 return ret;
95 void
96 PathBuilderCG::EnsureActive(const Point &aPoint)
100 TemporaryRef<Path>
101 PathBuilderCG::Finish()
103 return new PathCG(mCGPath, mFillRule);
106 TemporaryRef<PathBuilder>
107 PathCG::CopyToBuilder(FillRule aFillRule) const
109 CGMutablePathRef path = CGPathCreateMutableCopy(mPath);
110 return new PathBuilderCG(path, aFillRule);
115 TemporaryRef<PathBuilder>
116 PathCG::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
118 // 10.7 adds CGPathCreateMutableCopyByTransformingPath it might be faster than doing
119 // this by hand
121 struct TransformApplier {
122 CGMutablePathRef path;
123 CGAffineTransform transform;
124 static void
125 TranformCGPathApplierFunc(void *vinfo, const CGPathElement *element)
127 TransformApplier *info = reinterpret_cast<TransformApplier*>(vinfo);
128 switch (element->type) {
129 case kCGPathElementMoveToPoint:
131 CGPoint pt = element->points[0];
132 CGPathMoveToPoint(info->path, &info->transform, pt.x, pt.y);
133 break;
135 case kCGPathElementAddLineToPoint:
137 CGPoint pt = element->points[0];
138 CGPathAddLineToPoint(info->path, &info->transform, pt.x, pt.y);
139 break;
141 case kCGPathElementAddQuadCurveToPoint:
143 CGPoint cpt = element->points[0];
144 CGPoint pt = element->points[1];
145 CGPathAddQuadCurveToPoint(info->path, &info->transform, cpt.x, cpt.y, pt.x, pt.y);
146 break;
148 case kCGPathElementAddCurveToPoint:
150 CGPoint cpt1 = element->points[0];
151 CGPoint cpt2 = element->points[1];
152 CGPoint pt = element->points[2];
153 CGPathAddCurveToPoint(info->path, &info->transform, cpt1.x, cpt1.y, cpt2.x, cpt2.y, pt.x, pt.y);
154 break;
156 case kCGPathElementCloseSubpath:
158 CGPathCloseSubpath(info->path);
159 break;
165 TransformApplier ta;
166 ta.path = CGPathCreateMutable();
167 ta.transform = GfxMatrixToCGAffineTransform(aTransform);
169 CGPathApply(mPath, &ta, TransformApplier::TranformCGPathApplierFunc);
170 return new PathBuilderCG(ta.path, aFillRule);
173 static void
174 StreamPathToSinkApplierFunc(void *vinfo, const CGPathElement *element)
176 PathSink *sink = reinterpret_cast<PathSink*>(vinfo);
177 switch (element->type) {
178 case kCGPathElementMoveToPoint:
180 CGPoint pt = element->points[0];
181 sink->MoveTo(CGPointToPoint(pt));
182 break;
184 case kCGPathElementAddLineToPoint:
186 CGPoint pt = element->points[0];
187 sink->LineTo(CGPointToPoint(pt));
188 break;
190 case kCGPathElementAddQuadCurveToPoint:
192 CGPoint cpt = element->points[0];
193 CGPoint pt = element->points[1];
194 sink->QuadraticBezierTo(CGPointToPoint(cpt),
195 CGPointToPoint(pt));
196 break;
198 case kCGPathElementAddCurveToPoint:
200 CGPoint cpt1 = element->points[0];
201 CGPoint cpt2 = element->points[1];
202 CGPoint pt = element->points[2];
203 sink->BezierTo(CGPointToPoint(cpt1),
204 CGPointToPoint(cpt2),
205 CGPointToPoint(pt));
206 break;
208 case kCGPathElementCloseSubpath:
210 sink->Close();
211 break;
216 void
217 PathCG::StreamToSink(PathSink *aSink) const
219 CGPathApply(mPath, aSink, StreamPathToSinkApplierFunc);
222 bool
223 PathCG::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
225 Matrix inverse = aTransform;
226 inverse.Invert();
227 Point transformedPoint = inverse*aPoint;
228 // We could probably drop the input transform and just transform the point at the caller?
229 CGPoint point = {transformedPoint.x, transformedPoint.y};
231 // The transform parameter of CGPathContainsPoint doesn't seem to work properly on OS X 10.5
232 // so we transform aPoint ourselves.
233 return CGPathContainsPoint(mPath, nullptr, point, mFillRule == FillRule::FILL_EVEN_ODD);
236 static size_t
237 PutBytesNull(void *info, const void *buffer, size_t count)
239 return count;
242 /* The idea of a scratch context comes from WebKit */
243 static CGContextRef
244 CreateScratchContext()
246 CGDataConsumerCallbacks callbacks = {PutBytesNull, nullptr};
247 CGDataConsumerRef consumer = CGDataConsumerCreate(nullptr, &callbacks);
248 CGContextRef cg = CGPDFContextCreate(consumer, nullptr, nullptr);
249 CGDataConsumerRelease(consumer);
250 return cg;
253 static CGContextRef
254 ScratchContext()
256 static CGContextRef cg = CreateScratchContext();
257 return cg;
260 bool
261 PathCG::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
262 const Point &aPoint,
263 const Matrix &aTransform) const
265 Matrix inverse = aTransform;
266 inverse.Invert();
267 Point transformedPoint = inverse*aPoint;
268 // We could probably drop the input transform and just transform the point at the caller?
269 CGPoint point = {transformedPoint.x, transformedPoint.y};
271 CGContextRef cg = ScratchContext();
273 CGContextSaveGState(cg);
275 CGContextBeginPath(cg);
276 CGContextAddPath(cg, mPath);
278 SetStrokeOptions(cg, aStrokeOptions);
280 CGContextReplacePathWithStrokedPath(cg);
281 CGContextRestoreGState(cg);
283 CGPathRef sPath = CGContextCopyPath(cg);
284 bool inStroke = CGPathContainsPoint(sPath, nullptr, point, false);
285 CGPathRelease(sPath);
287 return inStroke;
290 //XXX: what should these functions return for an empty path?
291 // currently they return CGRectNull {inf,inf, 0, 0}
292 Rect
293 PathCG::GetBounds(const Matrix &aTransform) const
295 //XXX: are these bounds tight enough
296 Rect bounds = CGRectToRect(CGPathGetBoundingBox(mPath));
298 //XXX: currently this returns the bounds of the transformed bounds
299 // this is strictly looser than the bounds of the transformed path
300 return aTransform.TransformBounds(bounds);
303 Rect
304 PathCG::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
305 const Matrix &aTransform) const
307 // 10.7 has CGPathCreateCopyByStrokingPath which we could use
308 // instead of this scratch context business
309 CGContextRef cg = ScratchContext();
311 CGContextSaveGState(cg);
313 CGContextBeginPath(cg);
314 CGContextAddPath(cg, mPath);
316 SetStrokeOptions(cg, aStrokeOptions);
318 CGContextReplacePathWithStrokedPath(cg);
319 Rect bounds = CGRectToRect(CGContextGetPathBoundingBox(cg));
321 CGContextRestoreGState(cg);
323 if (!bounds.IsFinite()) {
324 return Rect();
327 return aTransform.TransformBounds(bounds);