Update configs. IGNORE BROKEN CHANGESETS CLOSED TREE NO BUG a=release ba=release
[gecko.git] / gfx / 2d / PathSkia.cpp
bloba7451d24ff593cb0db9eee14207e58652e6d5a31
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #include "PathSkia.h"
8 #include "HelpersSkia.h"
9 #include "PathHelpers.h"
10 #include "mozilla/UniquePtr.h"
11 #include "skia/include/core/SkPathUtils.h"
12 #include "skia/src/core/SkGeometry.h"
14 namespace mozilla::gfx {
16 already_AddRefed<PathBuilder> PathBuilderSkia::Create(FillRule aFillRule) {
17 return MakeAndAddRef<PathBuilderSkia>(aFillRule);
20 PathBuilderSkia::PathBuilderSkia(const Matrix& aTransform, const SkPath& aPath,
21 FillRule aFillRule)
22 : mPath(aPath) {
23 SkMatrix matrix;
24 GfxMatrixToSkiaMatrix(aTransform, matrix);
25 mPath.transform(matrix);
26 SetFillRule(aFillRule);
29 PathBuilderSkia::PathBuilderSkia(FillRule aFillRule) { SetFillRule(aFillRule); }
31 void PathBuilderSkia::SetFillRule(FillRule aFillRule) {
32 mFillRule = aFillRule;
33 if (mFillRule == FillRule::FILL_WINDING) {
34 mPath.setFillType(SkPathFillType::kWinding);
35 } else {
36 mPath.setFillType(SkPathFillType::kEvenOdd);
40 void PathBuilderSkia::MoveTo(const Point& aPoint) {
41 mPath.moveTo(SkFloatToScalar(aPoint.x), SkFloatToScalar(aPoint.y));
42 mCurrentPoint = aPoint;
43 mBeginPoint = aPoint;
46 void PathBuilderSkia::LineTo(const Point& aPoint) {
47 if (!mPath.countPoints()) {
48 MoveTo(aPoint);
49 } else {
50 mPath.lineTo(SkFloatToScalar(aPoint.x), SkFloatToScalar(aPoint.y));
52 mCurrentPoint = aPoint;
55 void PathBuilderSkia::BezierTo(const Point& aCP1, const Point& aCP2,
56 const Point& aCP3) {
57 if (!mPath.countPoints()) {
58 MoveTo(aCP1);
60 mPath.cubicTo(SkFloatToScalar(aCP1.x), SkFloatToScalar(aCP1.y),
61 SkFloatToScalar(aCP2.x), SkFloatToScalar(aCP2.y),
62 SkFloatToScalar(aCP3.x), SkFloatToScalar(aCP3.y));
63 mCurrentPoint = aCP3;
66 void PathBuilderSkia::QuadraticBezierTo(const Point& aCP1, const Point& aCP2) {
67 if (!mPath.countPoints()) {
68 MoveTo(aCP1);
70 mPath.quadTo(SkFloatToScalar(aCP1.x), SkFloatToScalar(aCP1.y),
71 SkFloatToScalar(aCP2.x), SkFloatToScalar(aCP2.y));
72 mCurrentPoint = aCP2;
75 void PathBuilderSkia::Close() {
76 mPath.close();
77 mCurrentPoint = mBeginPoint;
80 void PathBuilderSkia::Arc(const Point& aOrigin, float aRadius,
81 float aStartAngle, float aEndAngle,
82 bool aAntiClockwise) {
83 ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle,
84 aAntiClockwise);
87 already_AddRefed<Path> PathBuilderSkia::Finish() {
88 RefPtr<Path> path =
89 MakeAndAddRef<PathSkia>(mPath, mFillRule, mCurrentPoint, mBeginPoint);
90 mCurrentPoint = Point(0.0, 0.0);
91 mBeginPoint = Point(0.0, 0.0);
92 return path.forget();
95 void PathBuilderSkia::AppendPath(const SkPath& aPath) { mPath.addPath(aPath); }
97 already_AddRefed<PathBuilder> PathSkia::CopyToBuilder(
98 FillRule aFillRule) const {
99 return TransformedCopyToBuilder(Matrix(), aFillRule);
102 already_AddRefed<PathBuilder> PathSkia::TransformedCopyToBuilder(
103 const Matrix& aTransform, FillRule aFillRule) const {
104 RefPtr<PathBuilderSkia> builder =
105 MakeAndAddRef<PathBuilderSkia>(aTransform, mPath, aFillRule);
107 builder->mCurrentPoint = aTransform.TransformPoint(mCurrentPoint);
108 builder->mBeginPoint = aTransform.TransformPoint(mBeginPoint);
110 return builder.forget();
113 static bool SkPathContainsPoint(const SkPath& aPath, const Point& aPoint,
114 const Matrix& aTransform) {
115 Matrix inverse = aTransform;
116 if (!inverse.Invert()) {
117 return false;
120 SkPoint point = PointToSkPoint(inverse.TransformPoint(aPoint));
121 return aPath.contains(point.fX, point.fY);
124 bool PathSkia::ContainsPoint(const Point& aPoint,
125 const Matrix& aTransform) const {
126 if (!mPath.isFinite()) {
127 return false;
130 return SkPathContainsPoint(mPath, aPoint, aTransform);
133 bool PathSkia::GetFillPath(const StrokeOptions& aStrokeOptions,
134 const Matrix& aTransform, SkPath& aFillPath,
135 const Maybe<Rect>& aClipRect) const {
136 SkPaint paint;
137 if (!StrokeOptionsToPaint(paint, aStrokeOptions)) {
138 return false;
141 SkMatrix skiaMatrix;
142 GfxMatrixToSkiaMatrix(aTransform, skiaMatrix);
144 Maybe<SkRect> cullRect;
145 if (aClipRect.isSome()) {
146 cullRect = Some(RectToSkRect(aClipRect.ref()));
149 return skpathutils::FillPathWithPaint(mPath, paint, &aFillPath,
150 cullRect.ptrOr(nullptr), skiaMatrix);
153 bool PathSkia::StrokeContainsPoint(const StrokeOptions& aStrokeOptions,
154 const Point& aPoint,
155 const Matrix& aTransform) const {
156 if (!mPath.isFinite()) {
157 return false;
160 SkPath strokePath;
161 if (!GetFillPath(aStrokeOptions, aTransform, strokePath)) {
162 return false;
165 return SkPathContainsPoint(strokePath, aPoint, aTransform);
168 Rect PathSkia::GetBounds(const Matrix& aTransform) const {
169 if (!mPath.isFinite()) {
170 return Rect();
173 Rect bounds = SkRectToRect(mPath.computeTightBounds());
174 return aTransform.TransformBounds(bounds);
177 Rect PathSkia::GetStrokedBounds(const StrokeOptions& aStrokeOptions,
178 const Matrix& aTransform) const {
179 if (!mPath.isFinite()) {
180 return Rect();
183 SkPath fillPath;
184 if (!GetFillPath(aStrokeOptions, aTransform, fillPath)) {
185 return Rect();
188 Rect bounds = SkRectToRect(fillPath.computeTightBounds());
189 return aTransform.TransformBounds(bounds);
192 Rect PathSkia::GetFastBounds(const Matrix& aTransform,
193 const StrokeOptions* aStrokeOptions) const {
194 if (!mPath.isFinite()) {
195 return Rect();
197 SkRect bounds = mPath.getBounds();
198 if (aStrokeOptions) {
199 // If the path is stroked, ensure that the bounds are inflated by any
200 // relevant options such as line width. Avoid using dash path effects
201 // for performance and to ensure computeFastStrokeBounds succeeds.
202 SkPaint paint;
203 if (!StrokeOptionsToPaint(paint, *aStrokeOptions, false)) {
204 return Rect();
206 SkRect outBounds = SkRect::MakeEmpty();
207 bounds = paint.computeFastStrokeBounds(bounds, &outBounds);
209 return aTransform.TransformBounds(SkRectToRect(bounds));
212 int ConvertConicToQuads(const Point& aP0, const Point& aP1, const Point& aP2,
213 float aWeight, std::vector<Point>& aQuads) {
214 SkConic conic(PointToSkPoint(aP0), PointToSkPoint(aP1), PointToSkPoint(aP2),
215 aWeight);
216 int pow2 = conic.computeQuadPOW2(0.25f);
217 aQuads.resize(1 + 2 * (1 << pow2));
218 int numQuads =
219 conic.chopIntoQuadsPOW2(reinterpret_cast<SkPoint*>(&aQuads[0]), pow2);
220 if (numQuads < 1 << pow2) {
221 aQuads.resize(1 + 2 * numQuads);
223 return numQuads;
226 void PathSkia::StreamToSink(PathSink* aSink) const {
227 SkPath::RawIter iter(mPath);
229 SkPoint points[4];
230 SkPath::Verb currentVerb;
231 while ((currentVerb = iter.next(points)) != SkPath::kDone_Verb) {
232 switch (currentVerb) {
233 case SkPath::kMove_Verb:
234 aSink->MoveTo(SkPointToPoint(points[0]));
235 break;
236 case SkPath::kLine_Verb:
237 aSink->LineTo(SkPointToPoint(points[1]));
238 break;
239 case SkPath::kCubic_Verb:
240 aSink->BezierTo(SkPointToPoint(points[1]), SkPointToPoint(points[2]),
241 SkPointToPoint(points[3]));
242 break;
243 case SkPath::kQuad_Verb:
244 aSink->QuadraticBezierTo(SkPointToPoint(points[1]),
245 SkPointToPoint(points[2]));
246 break;
247 case SkPath::kConic_Verb: {
248 std::vector<Point> quads;
249 int numQuads = ConvertConicToQuads(
250 SkPointToPoint(points[0]), SkPointToPoint(points[1]),
251 SkPointToPoint(points[2]), iter.conicWeight(), quads);
252 for (int i = 0; i < numQuads; i++) {
253 aSink->QuadraticBezierTo(quads[2 * i + 1], quads[2 * i + 2]);
255 break;
257 case SkPath::kClose_Verb:
258 aSink->Close();
259 break;
260 default:
261 MOZ_ASSERT(false);
262 // Unexpected verb found in path!
267 Maybe<Rect> PathSkia::AsRect() const {
268 SkRect rect;
269 if (mPath.isRect(&rect)) {
270 return Some(SkRectToRect(rect));
272 return Nothing();
275 bool PathSkia::IsEmpty() const {
276 // Move/Close/Done segments are not included in the mask so as long as any
277 // flag is set, we know that the path is non-empty.
278 return mPath.getSegmentMasks() == 0;
281 } // namespace mozilla::gfx