Update configs. IGNORE BROKEN CHANGESETS CLOSED TREE NO BUG a=release ba=release
[gecko.git] / gfx / 2d / PathSkia.cpp
blobf7c03df2492adf2b0b655de78493a0316f1dedf8
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(SkPath&& aPath, FillRule aFillRule,
21 const Point& aCurrentPoint,
22 const Point& aBeginPoint)
23 : mPath(aPath) {
24 SetFillRule(aFillRule);
25 SetCurrentPoint(aCurrentPoint);
26 SetBeginPoint(aBeginPoint);
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 MakeAndAddRef<PathBuilderSkia>(SkPath(mPath), aFillRule, mCurrentPoint,
100 mBeginPoint);
103 already_AddRefed<PathBuilder> PathSkia::TransformedCopyToBuilder(
104 const Matrix& aTransform, FillRule aFillRule) const {
105 SkMatrix matrix;
106 GfxMatrixToSkiaMatrix(aTransform, matrix);
107 SkPath path(mPath);
108 path.transform(matrix);
109 return MakeAndAddRef<PathBuilderSkia>(
110 std::move(path), aFillRule, aTransform.TransformPoint(mCurrentPoint),
111 aTransform.TransformPoint(mBeginPoint));
114 already_AddRefed<PathBuilder> PathSkia::MoveToBuilder(FillRule aFillRule) {
115 return MakeAndAddRef<PathBuilderSkia>(std::move(mPath), aFillRule,
116 mCurrentPoint, mBeginPoint);
119 already_AddRefed<PathBuilder> PathSkia::TransformedMoveToBuilder(
120 const Matrix& aTransform, FillRule aFillRule) {
121 SkMatrix matrix;
122 GfxMatrixToSkiaMatrix(aTransform, matrix);
123 mPath.transform(matrix);
124 return MakeAndAddRef<PathBuilderSkia>(
125 std::move(mPath), aFillRule, aTransform.TransformPoint(mCurrentPoint),
126 aTransform.TransformPoint(mBeginPoint));
129 static bool SkPathContainsPoint(const SkPath& aPath, const Point& aPoint,
130 const Matrix& aTransform) {
131 Matrix inverse = aTransform;
132 if (!inverse.Invert()) {
133 return false;
136 SkPoint point = PointToSkPoint(inverse.TransformPoint(aPoint));
137 return aPath.contains(point.fX, point.fY);
140 bool PathSkia::ContainsPoint(const Point& aPoint,
141 const Matrix& aTransform) const {
142 if (!mPath.isFinite()) {
143 return false;
146 return SkPathContainsPoint(mPath, aPoint, aTransform);
149 bool PathSkia::GetFillPath(const StrokeOptions& aStrokeOptions,
150 const Matrix& aTransform, SkPath& aFillPath,
151 const Maybe<Rect>& aClipRect) const {
152 SkPaint paint;
153 if (!StrokeOptionsToPaint(paint, aStrokeOptions)) {
154 return false;
157 SkMatrix skiaMatrix;
158 GfxMatrixToSkiaMatrix(aTransform, skiaMatrix);
160 Maybe<SkRect> cullRect;
161 if (aClipRect.isSome()) {
162 cullRect = Some(RectToSkRect(aClipRect.ref()));
165 return skpathutils::FillPathWithPaint(mPath, paint, &aFillPath,
166 cullRect.ptrOr(nullptr), skiaMatrix);
169 bool PathSkia::StrokeContainsPoint(const StrokeOptions& aStrokeOptions,
170 const Point& aPoint,
171 const Matrix& aTransform) const {
172 if (!mPath.isFinite()) {
173 return false;
176 SkPath strokePath;
177 if (!GetFillPath(aStrokeOptions, aTransform, strokePath)) {
178 return false;
181 return SkPathContainsPoint(strokePath, aPoint, aTransform);
184 Rect PathSkia::GetBounds(const Matrix& aTransform) const {
185 if (!mPath.isFinite()) {
186 return Rect();
189 Rect bounds = SkRectToRect(mPath.computeTightBounds());
190 return aTransform.TransformBounds(bounds);
193 Rect PathSkia::GetStrokedBounds(const StrokeOptions& aStrokeOptions,
194 const Matrix& aTransform) const {
195 if (!mPath.isFinite()) {
196 return Rect();
199 SkPath fillPath;
200 if (!GetFillPath(aStrokeOptions, aTransform, fillPath)) {
201 return Rect();
204 Rect bounds = SkRectToRect(fillPath.computeTightBounds());
205 return aTransform.TransformBounds(bounds);
208 Rect PathSkia::GetFastBounds(const Matrix& aTransform,
209 const StrokeOptions* aStrokeOptions) const {
210 if (!mPath.isFinite()) {
211 return Rect();
213 SkRect bounds = mPath.getBounds();
214 if (aStrokeOptions) {
215 // If the path is stroked, ensure that the bounds are inflated by any
216 // relevant options such as line width. Avoid using dash path effects
217 // for performance and to ensure computeFastStrokeBounds succeeds.
218 SkPaint paint;
219 if (!StrokeOptionsToPaint(paint, *aStrokeOptions, false)) {
220 return Rect();
222 SkRect outBounds = SkRect::MakeEmpty();
223 bounds = paint.computeFastStrokeBounds(bounds, &outBounds);
225 return aTransform.TransformBounds(SkRectToRect(bounds));
228 int ConvertConicToQuads(const Point& aP0, const Point& aP1, const Point& aP2,
229 float aWeight, std::vector<Point>& aQuads) {
230 SkConic conic(PointToSkPoint(aP0), PointToSkPoint(aP1), PointToSkPoint(aP2),
231 aWeight);
232 int pow2 = conic.computeQuadPOW2(0.25f);
233 aQuads.resize(1 + 2 * (1 << pow2));
234 int numQuads =
235 conic.chopIntoQuadsPOW2(reinterpret_cast<SkPoint*>(&aQuads[0]), pow2);
236 if (numQuads < 1 << pow2) {
237 aQuads.resize(1 + 2 * numQuads);
239 return numQuads;
242 void PathSkia::StreamToSink(PathSink* aSink) const {
243 SkPath::RawIter iter(mPath);
245 SkPoint points[4];
246 SkPath::Verb currentVerb;
247 while ((currentVerb = iter.next(points)) != SkPath::kDone_Verb) {
248 switch (currentVerb) {
249 case SkPath::kMove_Verb:
250 aSink->MoveTo(SkPointToPoint(points[0]));
251 break;
252 case SkPath::kLine_Verb:
253 aSink->LineTo(SkPointToPoint(points[1]));
254 break;
255 case SkPath::kCubic_Verb:
256 aSink->BezierTo(SkPointToPoint(points[1]), SkPointToPoint(points[2]),
257 SkPointToPoint(points[3]));
258 break;
259 case SkPath::kQuad_Verb:
260 aSink->QuadraticBezierTo(SkPointToPoint(points[1]),
261 SkPointToPoint(points[2]));
262 break;
263 case SkPath::kConic_Verb: {
264 std::vector<Point> quads;
265 int numQuads = ConvertConicToQuads(
266 SkPointToPoint(points[0]), SkPointToPoint(points[1]),
267 SkPointToPoint(points[2]), iter.conicWeight(), quads);
268 for (int i = 0; i < numQuads; i++) {
269 aSink->QuadraticBezierTo(quads[2 * i + 1], quads[2 * i + 2]);
271 break;
273 case SkPath::kClose_Verb:
274 aSink->Close();
275 break;
276 default:
277 MOZ_ASSERT(false);
278 // Unexpected verb found in path!
283 Maybe<Rect> PathSkia::AsRect() const {
284 SkRect skiaRect;
285 if (mPath.isRect(&skiaRect)) {
286 Rect rect = SkRectToRect(skiaRect);
287 // Ensure that the conversion between Skia rect and Moz2D rect is not lossy
288 // due to floating-point precision errors.
289 if (RectToSkRect(rect) == skiaRect) {
290 return Some(rect);
293 return Nothing();
296 bool PathSkia::IsEmpty() const {
297 // Move/Close/Done segments are not included in the mask so as long as any
298 // flag is set, we know that the path is non-empty.
299 return mPath.getSegmentMasks() == 0;
302 } // namespace mozilla::gfx