Skip to main content

path_kit/
path.rs

1//! 路径类型,由 move/line/quad/cubic/close 组成的 2D 轮廓。
2//! Path type representing 2D contours built from move, line, quad, cubic, and close verbs.
3
4use crate::bridge::ffi;
5use crate::matrix::Matrix;
6use crate::path_fill_type::PathFillType;
7use crate::path_iter::PathIter;
8use crate::point::Point;
9use crate::rect::Rect;
10use crate::{Direction, RectCorner};
11use crate::rrect::RRect;
12use cxx::UniquePtr;
13
14/// 路径的 safe 封装,底层为 C++ `pk::SkPath`。
15/// Safe wrapper around `pk::SkPath`.
16pub struct Path {
17    inner: UniquePtr<ffi::Path>,
18}
19
20impl Path {
21    /// 创建空路径。Creates an empty path.
22    pub fn new() -> Self {
23        Self {
24            inner: ffi::path_new(),
25        }
26    }
27
28    /// 从已有路径复制。Creates a copy of the given path.
29    pub fn from_path(other: &Path) -> Self {
30        Self {
31            inner: ffi::path_clone(other.as_cpp_ref()),
32        }
33    }
34
35    pub(crate) fn from_unique_ptr(inner: UniquePtr<ffi::Path>) -> Self {
36        Self { inner }
37    }
38
39    pub(crate) fn as_cpp_ref(&self) -> &ffi::Path {
40        self.inner.as_ref().expect("Path")
41    }
42
43    pub(crate) fn pin_cpp_mut(&mut self) -> std::pin::Pin<&mut ffi::Path> {
44        self.inner.pin_mut()
45    }
46
47    /// 清空路径。Empties the path.
48    pub fn reset(&mut self) {
49        ffi::path_reset(self.pin_cpp_mut());
50    }
51
52    /// 清空几何并复位内部字段,与 [`Self::reset`] 类似,但 **保留** 已分配的 `SkPath` 内部缓冲区(适合反复构建同规模路径)。
53    /// Same as `SkPath::rewind()`: clears geometry and fields like `reset`, but retains storage for reuse.
54    pub fn rewind(&mut self) {
55        ffi::path_rewind(self.pin_cpp_mut());
56    }
57
58    /// 是否无动词数组(空路径)。与 `SkPath::isEmpty` 一致(`countVerbs() == 0`),注意与「无控制点」不同。
59    /// True when the path has no verbs (`countVerbs() == 0`), matching `SkPath::isEmpty` (not the same as zero points).
60    pub fn is_empty(&self) -> bool {
61        ffi::path_count_verbs(self.as_cpp_ref()) == 0
62    }
63
64    /// 点的数量。Returns the number of points in the path.
65    pub fn count_points(&self) -> i32 {
66        ffi::path_count_points(self.as_cpp_ref())
67    }
68
69    /// 动词的数量。Returns the number of verbs (move, line, quad, cubic, close).
70    pub fn count_verbs(&self) -> i32 {
71        ffi::path_count_verbs(self.as_cpp_ref())
72    }
73
74    /// 迭代路径中的动词与点。Iterates over path verbs and points.
75    ///
76    /// `force_close` 为 true 时,开放轮廓会生成隐式 close。
77    /// When `force_close` is true, open contours generate implicit close.
78    pub fn iter(&self, force_close: bool) -> PathIter<'_> {
79        PathIter::new(self, force_close)
80    }
81
82    /// 获取第 i 个点。Returns the point at index, or None if out of range.
83    pub fn get_point(&self, index: i32) -> Option<Point> {
84        let n = self.count_points();
85        if index >= 0 && index < n {
86            let mut pt = ffi::Point { fX: 0.0, fY: 0.0 };
87            ffi::path_get_point(self.as_cpp_ref(), index, &mut pt);
88            Some(pt.into())
89        } else {
90            None
91        }
92    }
93
94    /// 计算紧密包围盒。Returns the tight axis-aligned bounding box.
95    ///
96    /// 永不失败;空路径返回 (0,0,0,0)。对复杂曲线,`pathops_tight_bounds` 可能更精确但可能返回 None。
97    /// Never fails; empty path returns (0,0,0,0). For complex curves, `pathops_tight_bounds` may be more accurate but can return None.
98    pub fn tight_bounds(&self) -> Rect {
99        let mut bounds = ffi::Rect {
100            fLeft: 0.0,
101            fTop: 0.0,
102            fRight: 0.0,
103            fBottom: 0.0,
104        };
105        ffi::path_compute_tight_bounds(self.as_cpp_ref(), &mut bounds);
106        bounds.into()
107    }
108
109    /// 全部控制点的轴对齐包围盒(`SkPath::getBounds`),含 `move` 点;通常有缓存,直线轮廓下与 [`Self::tight_bounds`] 接近。
110    /// AABB of all path points (including moves); `SkPath::getBounds()`. Often cached; for lines, close to [`Self::tight_bounds`].
111    pub fn bounds(&self) -> Rect {
112        let mut r = ffi::Rect {
113            fLeft: 0.0,
114            fTop: 0.0,
115            fRight: 0.0,
116            fBottom: 0.0,
117        };
118        ffi::path_get_bounds(self.as_cpp_ref(), &mut r);
119        r.into()
120    }
121
122    /// 当且仅当所有控制点坐标均为有限值(无 `±∞`、无 NaN)时为真。`SkPath::isFinite`。
123    /// True if every stored coordinate is finite (no infinities or NaNs). `SkPath::isFinite`.
124    pub fn is_finite(&self) -> bool {
125        ffi::path_is_finite(self.as_cpp_ref())
126    }
127
128    /// 填充时是否为凸区域(可能按需计算并缓存)。`SkPath::isConvex`。
129    /// Whether the filled path is convex (computed lazily if needed). `SkPath::isConvex`.
130    pub fn is_convex(&self) -> bool {
131        ffi::path_is_convex(self.as_cpp_ref())
132    }
133
134    /// 若路径等价于标准椭圆/圆(四段圆锥闭合),返回 `Some` 外接矩形;否则 `None`。`SkPath::isOval`。
135    /// `Some(bounds)` if the path is a circle/oval; `None` otherwise. `SkPath::isOval`.
136    pub fn is_oval(&self) -> Option<Rect> {
137        let mut r = ffi::Rect {
138            fLeft: 0.0,
139            fTop: 0.0,
140            fRight: 0.0,
141            fBottom: 0.0,
142        };
143        if ffi::path_is_oval(self.as_cpp_ref(), &mut r) {
144            Some(r.into())
145        } else {
146            None
147        }
148    }
149
150    /// 若路径仅为 `move + line`(一条开线段),返回 `(起点, 终点)`;否则 `None`。`SkPath::isLine`。
151    /// If the path is one open line segment (`move` + `line`), returns endpoints; else `None`. `SkPath::isLine`.
152    pub fn is_line(&self) -> Option<(Point, Point)> {
153        let mut p0 = ffi::Point { fX: 0.0, fY: 0.0 };
154        let mut p1 = ffi::Point { fX: 0.0, fY: 0.0 };
155        if ffi::path_is_line(self.as_cpp_ref(), &mut p0, &mut p1) {
156            Some((p0.into(), p1.into()))
157        } else {
158            None
159        }
160    }
161
162    /// 按内部顺序拷贝所有控制点(与 [`Self::get_point`] 下标一致)。`SkPath::getPoints`。
163    /// Copies all stored points in order (same indexing as [`Self::get_point`]). `SkPath::getPoints`.
164    pub fn points(&self) -> Vec<Point> {
165        let mut v = Vec::new();
166        ffi::path_get_points_copy(self.as_cpp_ref(), &mut v);
167        v.into_iter().map(Point::from).collect()
168    }
169
170    /// 拷贝动词序列,每字节为 `pk::SkPathVerb` 枚举底层值(与 [`crate::PathVerb`] 取值一致)。`SkPath::getVerbs`。
171    /// Raw verb bytes matching `pk::SkPathVerb` / [`crate::PathVerb`]. `SkPath::getVerbs`.
172    pub fn verbs(&self) -> Vec<u8> {
173        let mut v = Vec::new();
174        ffi::path_get_verbs_copy(self.as_cpp_ref(), &mut v);
175        v
176    }
177
178    /// 为即将追加的点预留容量以减小程序分配(`extra_pt_count` ≤ 0 时忽略)。`SkPath::incReserve`。
179    /// Hints extra point capacity before building; no-op if `extra_pt_count <= 0`. `SkPath::incReserve`.
180    pub fn inc_reserve(&mut self, extra_pt_count: i32) {
181        ffi::path_inc_reserve(self.pin_cpp_mut(), extra_pt_count);
182    }
183
184    /// 是否可与 `other` 做逐点插值(动词种类与点数一致,含圆锥权重)。应先调用再 [`Self::try_interpolate`]。`SkPath::isInterpolatable`。
185    /// True if paths can be blended pointwise (verbs/point count/conic weights match). `SkPath::isInterpolatable`.
186    pub fn is_interpolatable_with(&self, other: &Path) -> bool {
187        ffi::path_is_interpolatable(self.as_cpp_ref(), other.as_cpp_ref())
188    }
189
190    /// 在 `self` 与 `ending` 之间插值:`weight` 为 1 偏向起点、0 偏向终点(可超出 \[0,1\])。不兼容返回 `None`。`SkPath::interpolate`。
191    /// Blends points between `self` (`weight` toward start) and `ending`; returns `None` if incompatible. `SkPath::interpolate`.
192    pub fn try_interpolate(&self, ending: &Path, weight: f32) -> Option<Path> {
193        let mut out = Path::new();
194        let ok = ffi::path_interpolate(
195            self.as_cpp_ref(),
196            ending.as_cpp_ref(),
197            weight,
198            out.pin_cpp_mut(),
199        );
200        if ok { Some(out) } else { None }
201    }
202
203    /// 点列末尾坐标;无任何点时 `None`。`SkPath::getLastPt`。
204    /// Last point in the point array, or `None` if empty. `SkPath::getLastPt`.
205    pub fn last_pt(&self) -> Option<Point> {
206        let mut p = ffi::Point { fX: 0.0, fY: 0.0 };
207        if ffi::path_get_last_pt(self.as_cpp_ref(), &mut p) {
208            Some(p.into())
209        } else {
210            None
211        }
212    }
213
214    /// 修改最后一个点;若当前无点则等价于 `move_to(x, y)`。`SkPath::setLastPt`。
215    /// Updates the last point, or inserts a `moveTo` if the path is empty. `SkPath::setLastPt`.
216    pub fn set_last_pt(&mut self, x: f32, y: f32) {
217        ffi::path_set_last_pt(self.pin_cpp_mut(), x, y);
218    }
219
220    /// 路径中出现过的段类型位或运算结果,与 `pk::SkPathSegmentMask` 一致:`1<<0` line、`1<<1` quad、`1<<2` conic、`1<<3` cubic。
221    /// Bitmask of segment kinds present (line/quad/conic/cubic). Matches `pk::SkPathSegmentMask`.
222    pub fn segment_masks(&self) -> u32 {
223        ffi::path_segment_masks(self.as_cpp_ref())
224    }
225
226    /// 是否包含多于一条独立轮廓(多于一个起始 `move` 所隐含的轮廓)。`SkPath::hasMultipleContours`。
227    /// True if the path has more than one contour. `SkPath::hasMultipleContours`.
228    pub fn has_multiple_contours(&self) -> bool {
229        ffi::path_has_multiple_contours(self.as_cpp_ref())
230    }
231
232    /// 追加 `src` 的几何,所有点加上 `(dx, dy)`;`extend == true` 时若当前轮廓未闭合会先接一条线再拼接(`kExtend_AddPathMode`)。`SkPath::addPath(src, dx, dy, mode)`。
233    /// Appends `src` translated by `(dx, dy)`; `extend` selects append vs extend mode. `SkPath::addPath`.
234    pub fn add_path_offset(&mut self, src: &Path, dx: f32, dy: f32, extend: bool) -> &mut Self {
235        ffi::path_add_path_offset(self.pin_cpp_mut(), src.as_cpp_ref(), dx, dy, extend);
236        self
237    }
238
239    /// 将 `src` 的 **第一段** 轮廓逆序追加到本路径(总是新开轮廓)。`SkPath::reverseAddPath`。
240    /// Appends the first contour of `src` in reverse. Always starts a new contour. `SkPath::reverseAddPath`.
241    pub fn reverse_add_path(&mut self, src: &Path) -> &mut Self {
242        ffi::path_reverse_add_path(self.pin_cpp_mut(), src.as_cpp_ref());
243        self
244    }
245
246    /// 以 O(1) 代价交换两条路径的内容(替换底层 `UniquePtr`,语义等价于 `SkPath::swap`)。
247    /// Exchanges path contents by swapping owning pointers (like `SkPath::swap`).
248    pub fn swap(&mut self, other: &mut Self) {
249        std::mem::swap(&mut self.inner, &mut other.inner);
250    }
251
252    /// 按 [`Matrix`] 就地变换所有点与权重(含透视矩阵行为,与 `SkPath::transform` 一致)。
253    /// Transforms geometry in place; matches `SkPath::transform(const SkMatrix&, this)`.
254    pub fn transform(&mut self, matrix: &Matrix) {
255        ffi::path_transform(self.pin_cpp_mut(), matrix.mat.as_slice());
256    }
257
258    /// 返回变换后的新路径;`self` 不变。等价于 `SkPath::makeTransform`。
259    /// Returns a transformed copy; leaves `self` unchanged. Like `SkPath::makeTransform`.
260    pub fn transformed(&self, matrix: &Matrix) -> Path {
261        let mut out = Path::new();
262        ffi::path_transform_to(self.as_cpp_ref(), matrix.mat.as_slice(), out.pin_cpp_mut());
263        out
264    }
265
266    /// 最后一段轮廓是否闭合。Returns true if the last contour ends with close().
267    pub fn is_last_contour_closed(&self) -> bool {
268        ffi::path_is_last_contour_closed(self.as_cpp_ref())
269    }
270
271    /// 保守判断是否包含矩形(可能将部分内含矩形判为 false)。
272    /// Conservatively tests rect containment; may return false for some contained rects.
273    ///
274    /// 适用于单段凸轮廓路径。Works for single convex contour paths.
275    pub fn conservatively_contains_rect(&self, rect: &Rect) -> bool {
276        let r: ffi::Rect = (*rect).into();
277        ffi::path_conservatively_contains_rect(self.as_cpp_ref(), &r)
278    }
279
280    /// 是否可表示为矩形。Returns Some((rect, is_closed)) if path is a rect, None otherwise.
281    pub fn is_rect(&self) -> Option<(Rect, bool)> {
282        let mut out_rect = ffi::Rect {
283            fLeft: 0.0,
284            fTop: 0.0,
285            fRight: 0.0,
286            fBottom: 0.0,
287        };
288        let mut is_closed = false;
289        let mut direction = Direction::Cw;
290        let ok = ffi::path_is_rect(
291            self.as_cpp_ref(),
292            &mut out_rect,
293            &mut is_closed,
294            &mut direction,
295        );
296        if ok {
297            Some((out_rect.into(), is_closed))
298        } else {
299            None
300        }
301    }
302
303    /// 是否包含点。Returns true if (x, y) is inside the filled path.
304    ///
305    /// 使用当前 [`fill_type`](Self::fill_type)(新建路径默认为 [`PathFillType::Winding`])。
306    /// Uses current [`fill_type`](Self::fill_type) (new paths default to [`PathFillType::Winding`]).
307    pub fn contains(&self, x: f32, y: f32) -> bool {
308        ffi::path_contains(self.as_cpp_ref(), x, y)
309    }
310
311    /// 当前填充规则。Current fill rule.
312    pub fn fill_type(&self) -> PathFillType {
313        ffi::path_fill_type_bits(self.as_cpp_ref())
314    }
315
316    /// 设置填充规则。Sets fill rule.
317    pub fn set_fill_type(&mut self, ft: PathFillType) {
318        ffi::path_set_fill_type_bits(self.pin_cpp_mut(), ft);
319    }
320
321    /// 是否为反色填充(`InverseWinding` / `InverseEvenOdd`)。
322    /// True if fill type is inverse winding or inverse even-odd.
323    pub fn is_inverse_fill_type(&self) -> bool {
324        self.fill_type().is_inverse()
325    }
326
327    /// 在「普通 / 反色」之间切换(`Winding` ↔ `InverseWinding`,`EvenOdd` ↔ `InverseEvenOdd`)。
328    /// Toggles between normal and inverse fill (winding/even-odd pairs).
329    pub fn toggle_inverse_fill_type(&mut self) {
330        ffi::path_toggle_inverse_fill_type(self.pin_cpp_mut());
331    }
332
333    // ---------- 构建方法 / Construction methods ----------
334
335    /// 移动到 (x, y),开始新轮廓。Moves to (x, y) and starts a new contour.
336    pub fn move_to(&mut self, x: f32, y: f32) -> &mut Self {
337        ffi::path_move_to(self.pin_cpp_mut(), x, y);
338        self
339    }
340
341    /// 画线到 (x, y)。Adds a line from current point to (x, y).
342    ///
343    /// 需先调用 `move_to`;否则 Skia 以 (0, 0) 为隐式起点。
344    /// Requires prior `move_to`; otherwise Skia uses (0, 0) as implicit start.
345    pub fn line_to(&mut self, x: f32, y: f32) -> &mut Self {
346        ffi::path_line_to(self.pin_cpp_mut(), x, y);
347        self
348    }
349
350    /// 二次贝塞尔曲线。Adds a quadratic bezier (control point, end point).
351    pub fn quad_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32) -> &mut Self {
352        ffi::path_quad_to(self.pin_cpp_mut(), x1, y1, x2, y2);
353        self
354    }
355
356    /// 三次贝塞尔曲线。Adds a cubic bezier (ctrl1, ctrl2, end point).
357    pub fn cubic_to(
358        &mut self,
359        x1: f32,
360        y1: f32,
361        x2: f32,
362        y2: f32,
363        x3: f32,
364        y3: f32,
365    ) -> &mut Self {
366        ffi::path_cubic_to(self.pin_cpp_mut(), x1, y1, x2, y2, x3, y3);
367        self
368    }
369
370    /// 从当前点到 `(x2,y2)`,控制点为 `(x1,y1)`,圆锥权重 `w`(`w==1` 时可能降为二次曲线)。`SkPath::conicTo`。
371    /// Conic from current point to `(x2,y2)` with control `(x1,y1)` and weight `w`. `SkPath::conicTo`.
372    pub fn conic_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, w: f32) -> &mut Self {
373        ffi::path_conic_to(self.pin_cpp_mut(), x1, y1, x2, y2, w);
374        self
375    }
376
377    /// 在 `(x1,y1)`、`(x2,y2)` 与 `radius` 约束下追加与当前点相切的圆弧(PostScript `arct`)。需已有轮廓起点。`SkPath::arcTo`。
378    /// Tangent arc through the tangent at `(x1,y1)` toward `(x2,y2)` with `radius` (PostScript `arct`). `SkPath::arcTo`.
379    pub fn arc_to(&mut self, x1: f32, y1: f32, x2: f32, y2: f32, radius: f32) -> &mut Self {
380        ffi::path_arc_to(self.pin_cpp_mut(), x1, y1, x2, y2, radius);
381        self
382    }
383
384    /// 以 `pts[0]` 为起点依次连线;`pts` 为空时行为与 Skia 一致(仅 `move` 等);`close == true` 时闭合轮廓。`SkPath::addPoly`。
385    /// Polyline from `pts[0]`; empty slice uses Skia `addPoly` semantics; optional close. `SkPath::addPoly`.
386    pub fn add_poly(&mut self, pts: &[Point], close: bool) -> &mut Self {
387        let ffi_pts: Vec<ffi::Point> = pts.iter().copied().map(Into::into).collect();
388        ffi::path_add_poly(self.pin_cpp_mut(), ffi_pts.as_slice(), close);
389        self
390    }
391
392    /// 闭合当前轮廓。Closes the current contour (line back to first point).
393    pub fn close(&mut self) -> &mut Self {
394        ffi::path_close(self.pin_cpp_mut());
395        self
396    }
397
398    /// 添加矩形。Adds a rectangle as a closed contour.
399    pub fn add_rect(&mut self, rect: &Rect, dir: Direction, start: RectCorner) -> &mut Self {
400        let r: ffi::Rect = (*rect).into();
401        ffi::path_add_rect(self.pin_cpp_mut(), &r, dir, start);
402        self
403    }
404
405    /// 添加椭圆(由矩形包围)。Adds an oval (ellipse) bounded by the given rect.
406    pub fn add_oval(&mut self, rect: &Rect, dir: Direction) -> &mut Self {
407        let r: ffi::Rect = (*rect).into();
408        ffi::path_add_oval(self.pin_cpp_mut(), &r, dir);
409        self
410    }
411
412    /// 添加椭圆,起始点由 `start`(四角之一)与 `dir` 决定,与矩形 `addRect` 角语义一致。`SkPath::addOval(rect, dir, start)`。
413    /// Adds an oval starting at corner `start`, wound per `dir`. `SkPath::addOval(rect, dir, start)`.
414    pub fn add_oval_with_start(
415        &mut self,
416        rect: &Rect,
417        dir: Direction,
418        start: RectCorner,
419    ) -> &mut Self {
420        let r: ffi::Rect = (*rect).into();
421        ffi::path_add_oval_start(self.pin_cpp_mut(), &r, dir, start);
422        self
423    }
424
425    /// 添加圆。Adds a circle centered at (cx, cy) with given radius.
426    ///
427    /// `radius` 应 ≥ 0;负值时 Skia 行为未定义。
428    /// `radius` should be ≥ 0; negative values have undefined Skia behavior.
429    pub fn add_circle(&mut self, cx: f32, cy: f32, radius: f32, dir: Direction) -> &mut Self {
430        ffi::path_add_circle(self.pin_cpp_mut(), cx, cy, radius, dir);
431        self
432    }
433
434    /// 添加圆角矩形。Adds a rounded rectangle (rx, ry = corner radii).
435    ///
436    /// `rx`, `ry` 应 ≥ 0。Should be ≥ 0.
437    pub fn add_round_rect(
438        &mut self,
439        rect: &Rect,
440        rx: f32,
441        ry: f32,
442        dir: Direction,
443    ) -> &mut Self {
444        let r: ffi::Rect = (*rect).into();
445        ffi::path_add_round_rect(self.pin_cpp_mut(), &r, rx, ry, dir);
446        self
447    }
448
449    /// 添加 RRect(支持四角独立半径)。Adds RRect with per-corner radii.
450    pub fn add_rrect(&mut self, rrect: &RRect, dir: Direction) -> &mut Self {
451        let rr = rrect.as_ffi();
452        ffi::path_add_rrect(self.pin_cpp_mut(), &rr, dir);
453        self
454    }
455
456    /// 添加 RRect 并指定起始角。Adds RRect with start corner.
457    pub fn add_rrect_with_start(
458        &mut self,
459        rrect: &RRect,
460        dir: Direction,
461        start: RectCorner,
462    ) -> &mut Self {
463        let rr = rrect.as_ffi();
464        ffi::path_add_rrect_start(self.pin_cpp_mut(), &rr, dir, start);
465        self
466    }
467
468    /// 路径是否可表示为 RRect。Returns Some(rrect) if path is an RRect, None otherwise.
469    pub fn is_rrect(&self) -> Option<RRect> {
470        let mut out = ffi::RRect {
471            fRect: ffi::Rect {
472                fLeft: 0.0,
473                fTop: 0.0,
474                fRight: 0.0,
475                fBottom: 0.0,
476            },
477            fRadii: [ffi::Point { fX: 0.0, fY: 0.0 }; 4],
478            fType: ffi::RRectType::Empty,
479        };
480        ffi::rrect_new_empty(&mut out);
481        let ok = ffi::path_is_rrect(self.as_cpp_ref(), &mut out);
482        if ok {
483            Some(RRect::from_ffi(out))
484        } else {
485            None
486        }
487    }
488
489    /// 内部 `ffi::Path` 引用(仅 crate 内使用)。Internal use only.
490    pub(crate) fn as_raw(&self) -> &ffi::Path {
491        self.as_cpp_ref()
492    }
493
494    /// 内部 `ffi::Path` 可变 Pin(仅 crate 内使用)。Internal use only.
495    pub(crate) fn as_raw_pin_mut(&mut self) -> std::pin::Pin<&mut ffi::Path> {
496        self.pin_cpp_mut()
497    }
498}
499
500impl Default for Path {
501    fn default() -> Self {
502        Self::new()
503    }
504}
505
506impl Clone for Path {
507    fn clone(&self) -> Self {
508        Self::from_path(self)
509    }
510}
511
512/// 路径相等性,与 `pk::SkPath::operator==` 一致(填充类型 + 动词/点/圆锥权重数据)。
513/// Path equality per `pk::SkPath::operator==` (fill type and path ref data).
514impl PartialEq for Path {
515    fn eq(&self, other: &Self) -> bool {
516        ffi::path_equals(self.as_cpp_ref(), other.as_cpp_ref())
517    }
518}
519
520impl Eq for Path {}
521
522impl std::fmt::Debug for Path {
523    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
524        f.debug_struct("Path")
525            .field("fill_type", &self.fill_type())
526            .field("points", &self.count_points())
527            .field("verbs", &self.count_verbs())
528            .field("bounds", &self.bounds())
529            .finish()
530    }
531}