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/. */
6 
7 #include "PathSkia.h"
8 #include <math.h>
9 #include "DrawTargetSkia.h"
10 #include "Logging.h"
11 #include "HelpersSkia.h"
12 #include "PathHelpers.h"
13 #include "skia/src/core/SkDraw.h"
14 
15 namespace mozilla::gfx {
16 
PathBuilderSkia(const Matrix & aTransform,const SkPath & aPath,FillRule aFillRule)17 PathBuilderSkia::PathBuilderSkia(const Matrix& aTransform, const SkPath& aPath,
18                                  FillRule aFillRule)
19     : mPath(aPath) {
20   SkMatrix matrix;
21   GfxMatrixToSkiaMatrix(aTransform, matrix);
22   mPath.transform(matrix);
23   SetFillRule(aFillRule);
24 }
25 
PathBuilderSkia(FillRule aFillRule)26 PathBuilderSkia::PathBuilderSkia(FillRule aFillRule) { SetFillRule(aFillRule); }
27 
SetFillRule(FillRule aFillRule)28 void PathBuilderSkia::SetFillRule(FillRule aFillRule) {
29   mFillRule = aFillRule;
30   if (mFillRule == FillRule::FILL_WINDING) {
31     mPath.setFillType(SkPath::kWinding_FillType);
32   } else {
33     mPath.setFillType(SkPath::kEvenOdd_FillType);
34   }
35 }
36 
MoveTo(const Point & aPoint)37 void PathBuilderSkia::MoveTo(const Point& aPoint) {
38   mPath.moveTo(SkFloatToScalar(aPoint.x), SkFloatToScalar(aPoint.y));
39   mCurrentPoint = aPoint;
40   mBeginPoint = aPoint;
41 }
42 
LineTo(const Point & aPoint)43 void PathBuilderSkia::LineTo(const Point& aPoint) {
44   if (!mPath.countPoints()) {
45     MoveTo(aPoint);
46   } else {
47     mPath.lineTo(SkFloatToScalar(aPoint.x), SkFloatToScalar(aPoint.y));
48   }
49   mCurrentPoint = aPoint;
50 }
51 
BezierTo(const Point & aCP1,const Point & aCP2,const Point & aCP3)52 void PathBuilderSkia::BezierTo(const Point& aCP1, const Point& aCP2,
53                                const Point& aCP3) {
54   if (!mPath.countPoints()) {
55     MoveTo(aCP1);
56   }
57   mPath.cubicTo(SkFloatToScalar(aCP1.x), SkFloatToScalar(aCP1.y),
58                 SkFloatToScalar(aCP2.x), SkFloatToScalar(aCP2.y),
59                 SkFloatToScalar(aCP3.x), SkFloatToScalar(aCP3.y));
60   mCurrentPoint = aCP3;
61 }
62 
QuadraticBezierTo(const Point & aCP1,const Point & aCP2)63 void PathBuilderSkia::QuadraticBezierTo(const Point& aCP1, const Point& aCP2) {
64   if (!mPath.countPoints()) {
65     MoveTo(aCP1);
66   }
67   mPath.quadTo(SkFloatToScalar(aCP1.x), SkFloatToScalar(aCP1.y),
68                SkFloatToScalar(aCP2.x), SkFloatToScalar(aCP2.y));
69   mCurrentPoint = aCP2;
70 }
71 
Close()72 void PathBuilderSkia::Close() {
73   mPath.close();
74   mCurrentPoint = mBeginPoint;
75 }
76 
Arc(const Point & aOrigin,float aRadius,float aStartAngle,float aEndAngle,bool aAntiClockwise)77 void PathBuilderSkia::Arc(const Point& aOrigin, float aRadius,
78                           float aStartAngle, float aEndAngle,
79                           bool aAntiClockwise) {
80   ArcToBezier(this, aOrigin, Size(aRadius, aRadius), aStartAngle, aEndAngle,
81               aAntiClockwise);
82 }
83 
Finish()84 already_AddRefed<Path> PathBuilderSkia::Finish() {
85   RefPtr<Path> path =
86       MakeAndAddRef<PathSkia>(mPath, mFillRule, mCurrentPoint, mBeginPoint);
87   mCurrentPoint = Point(0.0, 0.0);
88   mBeginPoint = Point(0.0, 0.0);
89   return path.forget();
90 }
91 
AppendPath(const SkPath & aPath)92 void PathBuilderSkia::AppendPath(const SkPath& aPath) { mPath.addPath(aPath); }
93 
CopyToBuilder(FillRule aFillRule) const94 already_AddRefed<PathBuilder> PathSkia::CopyToBuilder(
95     FillRule aFillRule) const {
96   return TransformedCopyToBuilder(Matrix(), aFillRule);
97 }
98 
TransformedCopyToBuilder(const Matrix & aTransform,FillRule aFillRule) const99 already_AddRefed<PathBuilder> PathSkia::TransformedCopyToBuilder(
100     const Matrix& aTransform, FillRule aFillRule) const {
101   RefPtr<PathBuilderSkia> builder =
102       MakeAndAddRef<PathBuilderSkia>(aTransform, mPath, aFillRule);
103 
104   builder->mCurrentPoint = aTransform.TransformPoint(mCurrentPoint);
105   builder->mBeginPoint = aTransform.TransformPoint(mBeginPoint);
106 
107   return builder.forget();
108 }
109 
SkPathContainsPoint(const SkPath & aPath,const Point & aPoint,const Matrix & aTransform)110 static bool SkPathContainsPoint(const SkPath& aPath, const Point& aPoint,
111                                 const Matrix& aTransform) {
112   Matrix inverse = aTransform;
113   if (!inverse.Invert()) {
114     return false;
115   }
116 
117   SkPoint point = PointToSkPoint(inverse.TransformPoint(aPoint));
118   return aPath.contains(point.fX, point.fY);
119 }
120 
ContainsPoint(const Point & aPoint,const Matrix & aTransform) const121 bool PathSkia::ContainsPoint(const Point& aPoint,
122                              const Matrix& aTransform) const {
123   if (!mPath.isFinite()) {
124     return false;
125   }
126 
127   return SkPathContainsPoint(mPath, aPoint, aTransform);
128 }
129 
StrokeContainsPoint(const StrokeOptions & aStrokeOptions,const Point & aPoint,const Matrix & aTransform) const130 bool PathSkia::StrokeContainsPoint(const StrokeOptions& aStrokeOptions,
131                                    const Point& aPoint,
132                                    const Matrix& aTransform) const {
133   if (!mPath.isFinite()) {
134     return false;
135   }
136 
137   SkPaint paint;
138   if (!StrokeOptionsToPaint(paint, aStrokeOptions)) {
139     return false;
140   }
141 
142   SkMatrix skiaMatrix;
143   GfxMatrixToSkiaMatrix(aTransform, skiaMatrix);
144   SkPath strokePath;
145   paint.getFillPath(mPath, &strokePath, nullptr,
146                     SkDraw::ComputeResScaleForStroking(skiaMatrix));
147 
148   return SkPathContainsPoint(strokePath, aPoint, aTransform);
149 }
150 
GetBounds(const Matrix & aTransform) const151 Rect PathSkia::GetBounds(const Matrix& aTransform) const {
152   if (!mPath.isFinite()) {
153     return Rect();
154   }
155 
156   Rect bounds = SkRectToRect(mPath.computeTightBounds());
157   return aTransform.TransformBounds(bounds);
158 }
159 
GetStrokedBounds(const StrokeOptions & aStrokeOptions,const Matrix & aTransform) const160 Rect PathSkia::GetStrokedBounds(const StrokeOptions& aStrokeOptions,
161                                 const Matrix& aTransform) const {
162   if (!mPath.isFinite()) {
163     return Rect();
164   }
165 
166   SkPaint paint;
167   if (!StrokeOptionsToPaint(paint, aStrokeOptions)) {
168     return Rect();
169   }
170 
171   SkPath result;
172   paint.getFillPath(mPath, &result);
173 
174   Rect bounds = SkRectToRect(result.computeTightBounds());
175   return aTransform.TransformBounds(bounds);
176 }
177 
StreamToSink(PathSink * aSink) const178 void PathSkia::StreamToSink(PathSink* aSink) const {
179   SkPath::RawIter iter(mPath);
180 
181   SkPoint points[4];
182   SkPath::Verb currentVerb;
183   while ((currentVerb = iter.next(points)) != SkPath::kDone_Verb) {
184     switch (currentVerb) {
185       case SkPath::kMove_Verb:
186         aSink->MoveTo(SkPointToPoint(points[0]));
187         break;
188       case SkPath::kLine_Verb:
189         aSink->LineTo(SkPointToPoint(points[1]));
190         break;
191       case SkPath::kCubic_Verb:
192         aSink->BezierTo(SkPointToPoint(points[1]), SkPointToPoint(points[2]),
193                         SkPointToPoint(points[3]));
194         break;
195       case SkPath::kQuad_Verb:
196         aSink->QuadraticBezierTo(SkPointToPoint(points[1]),
197                                  SkPointToPoint(points[2]));
198         break;
199       case SkPath::kClose_Verb:
200         aSink->Close();
201         break;
202       default:
203         MOZ_ASSERT(false);
204         // Unexpected verb found in path!
205     }
206   }
207 }
208 
209 }  // namespace mozilla::gfx
210