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/. */
8 #include "DrawTargetCG.h"
14 PathBuilderCG::~PathBuilderCG()
16 CGPathRelease(mCGPath
);
20 PathBuilderCG::MoveTo(const Point
&aPoint
)
22 CGPathMoveToPoint(mCGPath
, nullptr, aPoint
.x
, aPoint
.y
);
26 PathBuilderCG::LineTo(const Point
&aPoint
)
28 if (CGPathIsEmpty(mCGPath
))
31 CGPathAddLineToPoint(mCGPath
, nullptr, aPoint
.x
, aPoint
.y
);
35 PathBuilderCG::BezierTo(const Point
&aCP1
,
40 if (CGPathIsEmpty(mCGPath
))
42 CGPathAddCurveToPoint(mCGPath
, nullptr,
50 PathBuilderCG::QuadraticBezierTo(const Point
&aCP1
,
53 if (CGPathIsEmpty(mCGPath
))
55 CGPathAddQuadCurveToPoint(mCGPath
, nullptr,
61 PathBuilderCG::Close()
63 if (!CGPathIsEmpty(mCGPath
))
64 CGPathCloseSubpath(mCGPath
);
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
79 CGPathAddArc(mCGPath
, nullptr,
88 PathBuilderCG::CurrentPoint() const
90 CGPoint pt
= CGPathGetCurrentPoint(mCGPath
);
91 Point
ret(pt
.x
, pt
.y
);
96 PathBuilderCG::EnsureActive(const Point
&aPoint
)
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
121 struct TransformApplier
{
122 CGMutablePathRef path
;
123 CGAffineTransform transform
;
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
);
135 case kCGPathElementAddLineToPoint
:
137 CGPoint pt
= element
->points
[0];
138 CGPathAddLineToPoint(info
->path
, &info
->transform
, pt
.x
, pt
.y
);
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
);
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
);
156 case kCGPathElementCloseSubpath
:
158 CGPathCloseSubpath(info
->path
);
166 ta
.path
= CGPathCreateMutable();
167 ta
.transform
= GfxMatrixToCGAffineTransform(aTransform
);
169 CGPathApply(mPath
, &ta
, TransformApplier::TranformCGPathApplierFunc
);
170 return new PathBuilderCG(ta
.path
, aFillRule
);
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
));
184 case kCGPathElementAddLineToPoint
:
186 CGPoint pt
= element
->points
[0];
187 sink
->LineTo(CGPointToPoint(pt
));
190 case kCGPathElementAddQuadCurveToPoint
:
192 CGPoint cpt
= element
->points
[0];
193 CGPoint pt
= element
->points
[1];
194 sink
->QuadraticBezierTo(CGPointToPoint(cpt
),
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
),
208 case kCGPathElementCloseSubpath
:
217 PathCG::StreamToSink(PathSink
*aSink
) const
219 CGPathApply(mPath
, aSink
, StreamPathToSinkApplierFunc
);
223 PathCG::ContainsPoint(const Point
&aPoint
, const Matrix
&aTransform
) const
225 Matrix inverse
= aTransform
;
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
);
237 PutBytesNull(void *info
, const void *buffer
, size_t count
)
242 /* The idea of a scratch context comes from WebKit */
244 CreateScratchContext()
246 CGDataConsumerCallbacks callbacks
= {PutBytesNull
, nullptr};
247 CGDataConsumerRef consumer
= CGDataConsumerCreate(nullptr, &callbacks
);
248 CGContextRef cg
= CGPDFContextCreate(consumer
, nullptr, nullptr);
249 CGDataConsumerRelease(consumer
);
256 static CGContextRef cg
= CreateScratchContext();
261 PathCG::StrokeContainsPoint(const StrokeOptions
&aStrokeOptions
,
263 const Matrix
&aTransform
) const
265 Matrix inverse
= aTransform
;
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
);
290 //XXX: what should these functions return for an empty path?
291 // currently they return CGRectNull {inf,inf, 0, 0}
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
);
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()) {
327 return aTransform
.TransformBounds(bounds
);