gfx/2d/PathCG.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:d9569f5c5498
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/. */
5
6 #include "PathCG.h"
7 #include <math.h>
8 #include "DrawTargetCG.h"
9 #include "Logging.h"
10
11 namespace mozilla {
12 namespace gfx {
13
14 PathBuilderCG::~PathBuilderCG()
15 {
16 CGPathRelease(mCGPath);
17 }
18
19 void
20 PathBuilderCG::MoveTo(const Point &aPoint)
21 {
22 CGPathMoveToPoint(mCGPath, nullptr, aPoint.x, aPoint.y);
23 }
24
25 void
26 PathBuilderCG::LineTo(const Point &aPoint)
27 {
28 if (CGPathIsEmpty(mCGPath))
29 MoveTo(aPoint);
30 else
31 CGPathAddLineToPoint(mCGPath, nullptr, aPoint.x, aPoint.y);
32 }
33
34 void
35 PathBuilderCG::BezierTo(const Point &aCP1,
36 const Point &aCP2,
37 const Point &aCP3)
38 {
39
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);
46
47 }
48
49 void
50 PathBuilderCG::QuadraticBezierTo(const Point &aCP1,
51 const Point &aCP2)
52 {
53 if (CGPathIsEmpty(mCGPath))
54 MoveTo(aCP1);
55 CGPathAddQuadCurveToPoint(mCGPath, nullptr,
56 aCP1.x, aCP1.y,
57 aCP2.x, aCP2.y);
58 }
59
60 void
61 PathBuilderCG::Close()
62 {
63 if (!CGPathIsEmpty(mCGPath))
64 CGPathCloseSubpath(mCGPath);
65 }
66
67 void
68 PathBuilderCG::Arc(const Point &aOrigin, Float aRadius, Float aStartAngle,
69 Float aEndAngle, bool aAntiClockwise)
70 {
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);
85 }
86
87 Point
88 PathBuilderCG::CurrentPoint() const
89 {
90 CGPoint pt = CGPathGetCurrentPoint(mCGPath);
91 Point ret(pt.x, pt.y);
92 return ret;
93 }
94
95 void
96 PathBuilderCG::EnsureActive(const Point &aPoint)
97 {
98 }
99
100 TemporaryRef<Path>
101 PathBuilderCG::Finish()
102 {
103 RefPtr<PathCG> path = new PathCG(mCGPath, mFillRule);
104 return path;
105 }
106
107 TemporaryRef<PathBuilder>
108 PathCG::CopyToBuilder(FillRule aFillRule) const
109 {
110 CGMutablePathRef path = CGPathCreateMutableCopy(mPath);
111 RefPtr<PathBuilderCG> builder = new PathBuilderCG(path, aFillRule);
112 return builder;
113 }
114
115
116
117 TemporaryRef<PathBuilder>
118 PathCG::TransformedCopyToBuilder(const Matrix &aTransform, FillRule aFillRule) const
119 {
120 // 10.7 adds CGPathCreateMutableCopyByTransformingPath it might be faster than doing
121 // this by hand
122
123 struct TransformApplier {
124 CGMutablePathRef path;
125 CGAffineTransform transform;
126 static void
127 TranformCGPathApplierFunc(void *vinfo, const CGPathElement *element)
128 {
129 TransformApplier *info = reinterpret_cast<TransformApplier*>(vinfo);
130 switch (element->type) {
131 case kCGPathElementMoveToPoint:
132 {
133 CGPoint pt = element->points[0];
134 CGPathMoveToPoint(info->path, &info->transform, pt.x, pt.y);
135 break;
136 }
137 case kCGPathElementAddLineToPoint:
138 {
139 CGPoint pt = element->points[0];
140 CGPathAddLineToPoint(info->path, &info->transform, pt.x, pt.y);
141 break;
142 }
143 case kCGPathElementAddQuadCurveToPoint:
144 {
145 CGPoint cpt = element->points[0];
146 CGPoint pt = element->points[1];
147 CGPathAddQuadCurveToPoint(info->path, &info->transform, cpt.x, cpt.y, pt.x, pt.y);
148 break;
149 }
150 case kCGPathElementAddCurveToPoint:
151 {
152 CGPoint cpt1 = element->points[0];
153 CGPoint cpt2 = element->points[1];
154 CGPoint pt = element->points[2];
155 CGPathAddCurveToPoint(info->path, &info->transform, cpt1.x, cpt1.y, cpt2.x, cpt2.y, pt.x, pt.y);
156 break;
157 }
158 case kCGPathElementCloseSubpath:
159 {
160 CGPathCloseSubpath(info->path);
161 break;
162 }
163 }
164 }
165 };
166
167 TransformApplier ta;
168 ta.path = CGPathCreateMutable();
169 ta.transform = GfxMatrixToCGAffineTransform(aTransform);
170
171 CGPathApply(mPath, &ta, TransformApplier::TranformCGPathApplierFunc);
172 RefPtr<PathBuilderCG> builder = new PathBuilderCG(ta.path, aFillRule);
173 return builder;
174 }
175
176 static void
177 StreamPathToSinkApplierFunc(void *vinfo, const CGPathElement *element)
178 {
179 PathSink *sink = reinterpret_cast<PathSink*>(vinfo);
180 switch (element->type) {
181 case kCGPathElementMoveToPoint:
182 {
183 CGPoint pt = element->points[0];
184 sink->MoveTo(CGPointToPoint(pt));
185 break;
186 }
187 case kCGPathElementAddLineToPoint:
188 {
189 CGPoint pt = element->points[0];
190 sink->LineTo(CGPointToPoint(pt));
191 break;
192 }
193 case kCGPathElementAddQuadCurveToPoint:
194 {
195 CGPoint cpt = element->points[0];
196 CGPoint pt = element->points[1];
197 sink->QuadraticBezierTo(CGPointToPoint(cpt),
198 CGPointToPoint(pt));
199 break;
200 }
201 case kCGPathElementAddCurveToPoint:
202 {
203 CGPoint cpt1 = element->points[0];
204 CGPoint cpt2 = element->points[1];
205 CGPoint pt = element->points[2];
206 sink->BezierTo(CGPointToPoint(cpt1),
207 CGPointToPoint(cpt2),
208 CGPointToPoint(pt));
209 break;
210 }
211 case kCGPathElementCloseSubpath:
212 {
213 sink->Close();
214 break;
215 }
216 }
217 }
218
219 void
220 PathCG::StreamToSink(PathSink *aSink) const
221 {
222 CGPathApply(mPath, aSink, StreamPathToSinkApplierFunc);
223 }
224
225 bool
226 PathCG::ContainsPoint(const Point &aPoint, const Matrix &aTransform) const
227 {
228 Matrix inverse = aTransform;
229 inverse.Invert();
230 Point transformedPoint = inverse*aPoint;
231 // We could probably drop the input transform and just transform the point at the caller?
232 CGPoint point = {transformedPoint.x, transformedPoint.y};
233
234 // The transform parameter of CGPathContainsPoint doesn't seem to work properly on OS X 10.5
235 // so we transform aPoint ourselves.
236 return CGPathContainsPoint(mPath, nullptr, point, mFillRule == FillRule::FILL_EVEN_ODD);
237 }
238
239 static size_t
240 PutBytesNull(void *info, const void *buffer, size_t count)
241 {
242 return count;
243 }
244
245 /* The idea of a scratch context comes from WebKit */
246 static CGContextRef
247 CreateScratchContext()
248 {
249 CGDataConsumerCallbacks callbacks = {PutBytesNull, nullptr};
250 CGDataConsumerRef consumer = CGDataConsumerCreate(nullptr, &callbacks);
251 CGContextRef cg = CGPDFContextCreate(consumer, nullptr, nullptr);
252 CGDataConsumerRelease(consumer);
253 return cg;
254 }
255
256 static CGContextRef
257 ScratchContext()
258 {
259 static CGContextRef cg = CreateScratchContext();
260 return cg;
261 }
262
263 bool
264 PathCG::StrokeContainsPoint(const StrokeOptions &aStrokeOptions,
265 const Point &aPoint,
266 const Matrix &aTransform) const
267 {
268 Matrix inverse = aTransform;
269 inverse.Invert();
270 Point transformedPoint = inverse*aPoint;
271 // We could probably drop the input transform and just transform the point at the caller?
272 CGPoint point = {transformedPoint.x, transformedPoint.y};
273
274 CGContextRef cg = ScratchContext();
275
276 CGContextSaveGState(cg);
277
278 CGContextBeginPath(cg);
279 CGContextAddPath(cg, mPath);
280
281 SetStrokeOptions(cg, aStrokeOptions);
282
283 CGContextReplacePathWithStrokedPath(cg);
284 CGContextRestoreGState(cg);
285
286 CGPathRef sPath = CGContextCopyPath(cg);
287 bool inStroke = CGPathContainsPoint(sPath, nullptr, point, false);
288 CGPathRelease(sPath);
289
290 return inStroke;
291 }
292
293 //XXX: what should these functions return for an empty path?
294 // currently they return CGRectNull {inf,inf, 0, 0}
295 Rect
296 PathCG::GetBounds(const Matrix &aTransform) const
297 {
298 //XXX: are these bounds tight enough
299 Rect bounds = CGRectToRect(CGPathGetBoundingBox(mPath));
300
301 //XXX: currently this returns the bounds of the transformed bounds
302 // this is strictly looser than the bounds of the transformed path
303 return aTransform.TransformBounds(bounds);
304 }
305
306 Rect
307 PathCG::GetStrokedBounds(const StrokeOptions &aStrokeOptions,
308 const Matrix &aTransform) const
309 {
310 // 10.7 has CGPathCreateCopyByStrokingPath which we could use
311 // instead of this scratch context business
312 CGContextRef cg = ScratchContext();
313
314 CGContextSaveGState(cg);
315
316 CGContextBeginPath(cg);
317 CGContextAddPath(cg, mPath);
318
319 SetStrokeOptions(cg, aStrokeOptions);
320
321 CGContextReplacePathWithStrokedPath(cg);
322 Rect bounds = CGRectToRect(CGContextGetPathBoundingBox(cg));
323
324 CGContextRestoreGState(cg);
325
326 if (!bounds.IsFinite()) {
327 return Rect();
328 }
329
330 return aTransform.TransformBounds(bounds);
331 }
332
333
334 }
335
336 }

mercurial