1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "flutter/lib/ui/painting/path.h"
6
7#define _USE_MATH_DEFINES
8#include <math.h>
9
10#include "flutter/lib/ui/painting/matrix.h"
11#include "flutter/lib/ui/ui_dart_state.h"
12#include "third_party/tonic/converter/dart_converter.h"
13#include "third_party/tonic/dart_args.h"
14#include "third_party/tonic/dart_binding_macros.h"
15#include "third_party/tonic/dart_library_natives.h"
16
17using tonic::ToDart;
18
19namespace flutter {
20
21typedef CanvasPath Path;
22
23static void Path_constructor(Dart_NativeArguments args) {
24 UIDartState::ThrowIfUIOperationsProhibited();
25 DartCallConstructor(&CanvasPath::CreateNew, args);
26}
27
28IMPLEMENT_WRAPPERTYPEINFO(ui, Path);
29
30#define FOR_EACH_BINDING(V) \
31 V(Path, addArc) \
32 V(Path, addOval) \
33 V(Path, addPath) \
34 V(Path, addPolygon) \
35 V(Path, addRect) \
36 V(Path, addRRect) \
37 V(Path, arcTo) \
38 V(Path, arcToPoint) \
39 V(Path, close) \
40 V(Path, conicTo) \
41 V(Path, contains) \
42 V(Path, cubicTo) \
43 V(Path, extendWithPath) \
44 V(Path, extendWithPathAndMatrix) \
45 V(Path, getFillType) \
46 V(Path, lineTo) \
47 V(Path, moveTo) \
48 V(Path, quadraticBezierTo) \
49 V(Path, relativeArcToPoint) \
50 V(Path, relativeConicTo) \
51 V(Path, relativeCubicTo) \
52 V(Path, relativeLineTo) \
53 V(Path, relativeMoveTo) \
54 V(Path, relativeQuadraticBezierTo) \
55 V(Path, reset) \
56 V(Path, setFillType) \
57 V(Path, shift) \
58 V(Path, transform) \
59 V(Path, getBounds) \
60 V(Path, addPathWithMatrix) \
61 V(Path, op) \
62 V(Path, clone)
63
64FOR_EACH_BINDING(DART_NATIVE_CALLBACK)
65
66void CanvasPath::RegisterNatives(tonic::DartLibraryNatives* natives) {
67 natives->Register({{"Path_constructor", Path_constructor, 1, true},
68 FOR_EACH_BINDING(DART_REGISTER_NATIVE)});
69}
70
71CanvasPath::CanvasPath() {}
72
73CanvasPath::~CanvasPath() {}
74
75int CanvasPath::getFillType() {
76 return static_cast<int>(path_.getFillType());
77}
78
79void CanvasPath::setFillType(int fill_type) {
80 path_.setFillType(static_cast<SkPathFillType>(fill_type));
81}
82
83void CanvasPath::moveTo(float x, float y) {
84 path_.moveTo(x, y);
85}
86
87void CanvasPath::relativeMoveTo(float x, float y) {
88 path_.rMoveTo(x, y);
89}
90
91void CanvasPath::lineTo(float x, float y) {
92 path_.lineTo(x, y);
93}
94
95void CanvasPath::relativeLineTo(float x, float y) {
96 path_.rLineTo(x, y);
97}
98
99void CanvasPath::quadraticBezierTo(float x1, float y1, float x2, float y2) {
100 path_.quadTo(x1, y1, x2, y2);
101}
102
103void CanvasPath::relativeQuadraticBezierTo(float x1,
104 float y1,
105 float x2,
106 float y2) {
107 path_.rQuadTo(x1, y1, x2, y2);
108}
109
110void CanvasPath::cubicTo(float x1,
111 float y1,
112 float x2,
113 float y2,
114 float x3,
115 float y3) {
116 path_.cubicTo(x1, y1, x2, y2, x3, y3);
117}
118
119void CanvasPath::relativeCubicTo(float x1,
120 float y1,
121 float x2,
122 float y2,
123 float x3,
124 float y3) {
125 path_.rCubicTo(x1, y1, x2, y2, x3, y3);
126}
127
128void CanvasPath::conicTo(float x1, float y1, float x2, float y2, float w) {
129 path_.conicTo(x1, y1, x2, y2, w);
130}
131
132void CanvasPath::relativeConicTo(float x1,
133 float y1,
134 float x2,
135 float y2,
136 float w) {
137 path_.rConicTo(x1, y1, x2, y2, w);
138}
139
140void CanvasPath::arcTo(float left,
141 float top,
142 float right,
143 float bottom,
144 float startAngle,
145 float sweepAngle,
146 bool forceMoveTo) {
147 path_.arcTo(SkRect::MakeLTRB(left, top, right, bottom),
148 startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI,
149 forceMoveTo);
150}
151
152void CanvasPath::arcToPoint(float arcEndX,
153 float arcEndY,
154 float radiusX,
155 float radiusY,
156 float xAxisRotation,
157 bool isLargeArc,
158 bool isClockwiseDirection) {
159 const auto arcSize = isLargeArc ? SkPath::ArcSize::kLarge_ArcSize
160 : SkPath::ArcSize::kSmall_ArcSize;
161 const auto direction =
162 isClockwiseDirection ? SkPathDirection::kCW : SkPathDirection::kCCW;
163
164 path_.arcTo(radiusX, radiusY, xAxisRotation, arcSize, direction, arcEndX,
165 arcEndY);
166}
167
168void CanvasPath::relativeArcToPoint(float arcEndDeltaX,
169 float arcEndDeltaY,
170 float radiusX,
171 float radiusY,
172 float xAxisRotation,
173 bool isLargeArc,
174 bool isClockwiseDirection) {
175 const auto arcSize = isLargeArc ? SkPath::ArcSize::kLarge_ArcSize
176 : SkPath::ArcSize::kSmall_ArcSize;
177 const auto direction =
178 isClockwiseDirection ? SkPathDirection::kCW : SkPathDirection::kCCW;
179 path_.rArcTo(radiusX, radiusY, xAxisRotation, arcSize, direction,
180 arcEndDeltaX, arcEndDeltaY);
181}
182
183void CanvasPath::addRect(float left, float top, float right, float bottom) {
184 path_.addRect(SkRect::MakeLTRB(left, top, right, bottom));
185}
186
187void CanvasPath::addOval(float left, float top, float right, float bottom) {
188 path_.addOval(SkRect::MakeLTRB(left, top, right, bottom));
189}
190
191void CanvasPath::addArc(float left,
192 float top,
193 float right,
194 float bottom,
195 float startAngle,
196 float sweepAngle) {
197 path_.addArc(SkRect::MakeLTRB(left, top, right, bottom),
198 startAngle * 180.0 / M_PI, sweepAngle * 180.0 / M_PI);
199}
200
201void CanvasPath::addPolygon(const tonic::Float32List& points, bool close) {
202 path_.addPoly(reinterpret_cast<const SkPoint*>(points.data()),
203 points.num_elements() / 2, close);
204}
205
206void CanvasPath::addRRect(const RRect& rrect) {
207 path_.addRRect(rrect.sk_rrect);
208}
209
210void CanvasPath::addPath(CanvasPath* path, double dx, double dy) {
211 if (!path) {
212 Dart_ThrowException(ToDart("Path.addPath called with non-genuine Path."));
213 return;
214 }
215 path_.addPath(path->path(), dx, dy, SkPath::kAppend_AddPathMode);
216}
217
218void CanvasPath::addPathWithMatrix(CanvasPath* path,
219 double dx,
220 double dy,
221 tonic::Float64List& matrix4) {
222 if (!path) {
223 Dart_ThrowException(
224 ToDart("Path.addPathWithMatrix called with non-genuine Path."));
225 return;
226 }
227
228 SkMatrix matrix = ToSkMatrix(matrix4);
229 matrix.setTranslateX(matrix.getTranslateX() + dx);
230 matrix.setTranslateY(matrix.getTranslateY() + dy);
231 path_.addPath(path->path(), matrix, SkPath::kAppend_AddPathMode);
232 matrix4.Release();
233}
234
235void CanvasPath::extendWithPath(CanvasPath* path, double dx, double dy) {
236 if (!path) {
237 Dart_ThrowException(
238 ToDart("Path.extendWithPath called with non-genuine Path."));
239 return;
240 }
241 path_.addPath(path->path(), dx, dy, SkPath::kExtend_AddPathMode);
242}
243
244void CanvasPath::extendWithPathAndMatrix(CanvasPath* path,
245 double dx,
246 double dy,
247 tonic::Float64List& matrix4) {
248 if (!path) {
249 Dart_ThrowException(
250 ToDart("Path.addPathWithMatrix called with non-genuine Path."));
251 return;
252 }
253
254 SkMatrix matrix = ToSkMatrix(matrix4);
255 matrix.setTranslateX(matrix.getTranslateX() + dx);
256 matrix.setTranslateY(matrix.getTranslateY() + dy);
257 path_.addPath(path->path(), matrix, SkPath::kExtend_AddPathMode);
258 matrix4.Release();
259}
260
261void CanvasPath::close() {
262 path_.close();
263}
264
265void CanvasPath::reset() {
266 path_.reset();
267}
268
269bool CanvasPath::contains(double x, double y) {
270 return path_.contains(x, y);
271}
272
273void CanvasPath::shift(Dart_Handle path_handle, double dx, double dy) {
274 fml::RefPtr<CanvasPath> path = CanvasPath::Create(path_handle);
275 path_.offset(dx, dy, &path->path_);
276}
277
278void CanvasPath::transform(Dart_Handle path_handle,
279 tonic::Float64List& matrix4) {
280 fml::RefPtr<CanvasPath> path = CanvasPath::Create(path_handle);
281 path_.transform(ToSkMatrix(matrix4), &path->path_);
282 matrix4.Release();
283}
284
285tonic::Float32List CanvasPath::getBounds() {
286 tonic::Float32List rect(Dart_NewTypedData(Dart_TypedData_kFloat32, 4));
287 const SkRect& bounds = path_.getBounds();
288 rect[0] = bounds.left();
289 rect[1] = bounds.top();
290 rect[2] = bounds.right();
291 rect[3] = bounds.bottom();
292 return rect;
293}
294
295bool CanvasPath::op(CanvasPath* path1, CanvasPath* path2, int operation) {
296 return Op(path1->path(), path2->path(), static_cast<SkPathOp>(operation),
297 &path_);
298}
299
300void CanvasPath::clone(Dart_Handle path_handle) {
301 fml::RefPtr<CanvasPath> path = CanvasPath::Create(path_handle);
302 // per Skia docs, this will create a fast copy
303 // data is shared until the source path or dest path are mutated
304 path->path_ = path_;
305}
306
307// This is doomed to be called too early, since Paths are mutable.
308// However, it can help for some of the clone/shift/transform type methods
309// where the resultant path will initially have a meaningful size.
310size_t CanvasPath::GetAllocationSize() const {
311 return sizeof(CanvasPath) + path_.approximateBytesUsed();
312}
313
314} // namespace flutter
315