1//! Wrapper for pathops/SkPathOps.h
2use crate::{prelude::*, Path, Rect};
3use skia_bindings::{self as sb, SkOpBuilder};
4use std::fmt;
5
6pub use skia_bindings::SkPathOp as PathOp;
7variant_name!(PathOp::XOR);
8
9// TODO: I am not so sure if we should export these global functions.
10
11pub fn op(one: &Path, two: &Path, op: PathOp) -> Option<Path> {
12 let mut result: Handle = Path::default();
13 unsafe { sb::Op(one:one.native(), two:two.native(), op, result:result.native_mut()) }.if_true_some(result)
14}
15
16pub fn simplify(path: &Path) -> Option<Path> {
17 let mut result: Handle = Path::default();
18 unsafe { sb::Simplify(path:path.native(), result:result.native_mut()) }.if_true_some(result)
19}
20
21pub fn tight_bounds(path: &Path) -> Option<Rect> {
22 let mut result: Rect = Rect::default();
23 unsafe { sb::TightBounds(path:path.native(), result:result.native_mut()) }.if_true_some(result)
24}
25
26pub fn as_winding(path: &Path) -> Option<Path> {
27 let mut result: Handle = Path::default();
28 unsafe { sb::AsWinding(path:path.native(), result:result.native_mut()) }.if_true_some(result)
29}
30
31pub type OpBuilder = Handle<SkOpBuilder>;
32unsafe_send_sync!(OpBuilder);
33
34impl NativeDrop for SkOpBuilder {
35 fn drop(&mut self) {
36 unsafe { sb::C_SkOpBuilder_destruct(self) }
37 }
38}
39
40impl Default for Handle<SkOpBuilder> {
41 fn default() -> Self {
42 Self::construct(|opb: *mut SkOpBuilder| unsafe { sb::C_SkOpBuilder_Construct(uninitialized:opb) })
43 }
44}
45
46impl fmt::Debug for OpBuilder {
47 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
48 f.debug_struct(name:"OpBuilder").finish()
49 }
50}
51
52impl OpBuilder {
53 pub fn add(&mut self, path: &Path, operator: PathOp) -> &mut Self {
54 unsafe {
55 self.native_mut().add(path:path.native(), operator);
56 }
57 self
58 }
59
60 pub fn resolve(&mut self) -> Option<Path> {
61 let mut path: Handle = Path::default();
62 unsafe { self.native_mut().resolve(result:path.native_mut()) }.if_true_some(path)
63 }
64}
65
66impl Path {
67 pub fn op(&self, path: &Path, path_op: PathOp) -> Option<Self> {
68 op(self, two:path, path_op)
69 }
70
71 pub fn simplify(&self) -> Option<Self> {
72 simplify(self)
73 }
74
75 pub fn tight_bounds(&self) -> Option<Rect> {
76 tight_bounds(self)
77 }
78
79 pub fn as_winding(&self) -> Option<Path> {
80 as_winding(self)
81 }
82}
83
84#[test]
85fn test_tight_bounds() {
86 let mut path: Handle = Path::new();
87 path.add_rect(Rect::from_point_and_size((10.0, 10.0), (10.0, 10.0)), dir_start:None);
88 path.add_rect(Rect::from_point_and_size((15.0, 15.0), (10.0, 10.0)), dir_start:None);
89 let tight_bounds: Rect = Rect::from_point_and_size((10.0, 10.0), (15.0, 15.0));
90 assert_eq!(path.tight_bounds().unwrap(), tight_bounds);
91}
92
93#[test]
94fn test_union() {
95 let mut path: Handle = Path::new();
96 path.add_rect(Rect::from_point_and_size((10.0, 10.0), (10.0, 10.0)), dir_start:None);
97 let mut path2: Handle = Path::new();
98 path2.add_rect(Rect::from_point_and_size((15.0, 15.0), (10.0, 10.0)), dir_start:None);
99 let union: Handle = path.op(&path2, PathOp::Union).unwrap();
100 let expected: Rect = Rect::from_point_and_size((10.0, 10.0), (15.0, 15.0));
101 assert_eq!(union.tight_bounds().unwrap(), expected);
102}
103
104#[test]
105fn test_intersect() {
106 let mut path: Handle = Path::new();
107 path.add_rect(Rect::from_point_and_size((10.0, 10.0), (10.0, 10.0)), dir_start:None);
108 let mut path2: Handle = Path::new();
109 path2.add_rect(Rect::from_point_and_size((15.0, 15.0), (10.0, 10.0)), dir_start:None);
110 let intersected: Handle = path.op(&path2, PathOp::Intersect).unwrap();
111 let expected: Rect = Rect::from_point_and_size((15.0, 15.0), (5.0, 5.0));
112 assert_eq!(intersected.tight_bounds().unwrap(), expected);
113}
114