1- use std:: borrow:: BorrowMut ;
2-
3- use skia_safe:: { ClipOp , Matrix , Point } ;
4-
51use crate :: context:: drawing_paths:: fill_rule:: FillRule ;
62use crate :: context:: paths:: path:: Path ;
73use crate :: context:: Context ;
4+ use skia_safe:: { ClipOp , Color , Matrix , Point } ;
85
96pub mod fill_rule;
107
@@ -15,55 +12,133 @@ impl Context {
1512 path : Option < & mut Path > ,
1613 fill_rule : Option < FillRule > ,
1714 ) {
15+ #[ cfg( any( target_os = "macos" , target_os = "ios" ) ) ]
16+ let _ = unsafe { objc2_foundation:: NSAutoreleasePool :: new ( ) } ;
17+
1818 #[ cfg( feature = "gl" ) ]
1919 {
2020 if let Some ( ref context) = self . gl_context {
2121 context. make_current ( ) ;
2222 }
2323 }
2424
25+ let current_rule = match path {
26+ None => self . path . fill_type ( ) ,
27+ Some ( ref path) => path. fill_type ( ) ,
28+ } ;
29+
30+ let fill_rule = fill_rule. unwrap_or ( FillRule :: default ( ) ) ;
31+
32+ let has_shadow = self . state . shadow_color != Color :: TRANSPARENT
33+ && self . state . shadow_blur > 0.0 ;
34+
35+ if has_shadow {
36+ // Slow path: need to save/modify/restore image filter + clone paint
37+ self . fill_or_stroke_with_shadow ( is_fill, path, fill_rule, current_rule) ;
38+ } else {
39+ // Fast path: no shadow — skip all shadow overhead.
40+ let paint = if is_fill {
41+ self . state . paint . fill_paint . clone ( )
42+ } else {
43+ self . state . paint . stroke_paint . clone ( )
44+ } ;
45+
46+ let target_rule = fill_rule. to_fill_type ( ) ;
47+ let needs_rule_change = current_rule != target_rule;
48+
49+ match path {
50+ Some ( path) => {
51+ if needs_rule_change {
52+ path. set_fill_type_raw ( target_rule) ;
53+ }
54+ self . render_to_canvas ( & paint, |canvas, paint| {
55+ canvas. draw_path ( path. path ( ) , paint) ;
56+ } ) ;
57+ if needs_rule_change {
58+ path. set_fill_type_raw ( current_rule) ;
59+ }
60+ }
61+ None => {
62+ if needs_rule_change {
63+ self . path . set_fill_type_raw ( target_rule) ;
64+ }
65+ self . render_to_canvas_with_path ( & paint, |canvas, paint, path| {
66+ canvas. draw_path ( path. path ( ) , paint) ;
67+ } ) ;
68+ if needs_rule_change {
69+ self . path . set_fill_type_raw ( current_rule) ;
70+ }
71+ }
72+ } ;
73+ }
74+ }
75+
76+ #[ cold]
77+ fn fill_or_stroke_with_shadow (
78+ & mut self ,
79+ is_fill : bool ,
80+ path : Option < & mut Path > ,
81+ fill_rule : FillRule ,
82+ current_rule : skia_safe:: PathFillType ,
83+ ) {
84+ let saved_image_filter = if is_fill {
85+ self . state . paint . fill_paint . image_filter ( )
86+ } else {
87+ self . state . paint . stroke_paint . image_filter ( )
88+ } ;
89+
90+ let current_image_filter = if is_fill {
91+ self . state . paint . fill_paint . image_filter ( )
92+ } else {
93+ self . state . paint . stroke_paint . image_filter ( )
94+ } ;
95+
96+ let sigma = self . state . shadow_blur / 2. ;
97+
98+ if let Some ( shadow) = skia_safe:: image_filters:: drop_shadow_only (
99+ self . state . shadow_offset ,
100+ ( sigma, sigma) ,
101+ self . state . shadow_color ,
102+ None ,
103+ current_image_filter,
104+ None ,
105+ ) {
106+ if is_fill {
107+ self . state . paint . fill_paint . set_image_filter ( shadow) ;
108+ } else {
109+ self . state . paint . stroke_paint . set_image_filter ( shadow) ;
110+ } ;
111+ }
112+
25113 let paint = if is_fill {
26114 self . state . paint . fill_paint . clone ( )
27115 } else {
28116 self . state . paint . stroke_paint . clone ( )
29117 } ;
30118
31- let fill_rule = fill_rule. unwrap_or ( FillRule :: default ( ) ) ;
32-
33- let path = match path {
119+ match path {
34120 Some ( path) => {
35- path. 0 . set_fill_type ( fill_rule. to_fill_type ( ) ) ;
36- path. 0 . clone ( )
121+ path. set_fill_type_raw ( fill_rule. to_fill_type ( ) ) ;
122+ self . render_to_canvas ( & paint, |canvas, paint| {
123+ canvas. draw_path ( path. path ( ) , paint) ;
124+ } ) ;
125+ path. set_fill_type_raw ( current_rule) ;
37126 }
38127 None => {
39- self . path . 0 . set_fill_type ( fill_rule. to_fill_type ( ) ) ;
40- self . path . 0 . clone ( )
128+ self . path . set_fill_type_raw ( fill_rule. to_fill_type ( ) ) ;
129+ self . render_to_canvas_with_path ( & paint, |canvas, paint, path| {
130+ canvas. draw_path ( path. path ( ) , paint) ;
131+ } ) ;
132+ self . path . set_fill_type_raw ( current_rule) ;
41133 }
42134 } ;
43135
44- let shadow_paint = if is_fill {
45- self . state . paint . fill_shadow_paint (
46- self . state . shadow_offset ,
47- self . state . shadow_color ,
48- self . state . shadow_blur ,
49- )
136+ // Restore the saved image filter
137+ if is_fill {
138+ self . state . paint . fill_paint . set_image_filter ( saved_image_filter) ;
50139 } else {
51- self . state . paint . stroke_shadow_paint (
52- self . state . shadow_offset ,
53- self . state . shadow_color ,
54- self . state . shadow_blur ,
55- )
140+ self . state . paint . stroke_paint . set_image_filter ( saved_image_filter) ;
56141 } ;
57-
58- #[ cfg( any( target_os = "macos" , target_os = "ios" ) ) ]
59- let pool = unsafe { objc2_foundation:: NSAutoreleasePool :: new ( ) } ;
60-
61- self . render_to_canvas ( & paint, |canvas, paint| {
62- if let Some ( paint) = & shadow_paint {
63- canvas. draw_path ( & path, paint) ;
64- }
65- canvas. draw_path ( & path, paint) ;
66- } ) ;
67142 }
68143
69144 pub fn fill ( & mut self , path : Option < & mut Path > ) {
@@ -79,52 +154,62 @@ impl Context {
79154 }
80155
81156 pub fn clip ( & mut self , path : Option < & mut Path > , fill_rule : Option < FillRule > ) {
82- let mut path = path
83- . map ( |path| path. clone ( ) )
84- . unwrap_or_else ( || self . path . clone ( ) ) ;
85-
86- path. set_fill_type ( fill_rule. unwrap_or ( FillRule :: NonZero ) ) ;
87-
88- self . surface
89- . canvas ( )
90- . clip_path ( path. path ( ) , Some ( ClipOp :: Intersect ) , Some ( true ) ) ;
157+ let fill_rule = fill_rule. unwrap_or ( FillRule :: NonZero ) ;
158+ match path {
159+ Some ( path) => {
160+ let current = path. fill_type ( ) ;
161+ path. set_fill_type ( fill_rule) ;
162+ self . surface
163+ . canvas ( )
164+ . clip_path ( path. path ( ) , Some ( ClipOp :: Intersect ) , Some ( true ) ) ;
165+ path. set_fill_type_raw ( current) ;
166+ }
167+ None => {
168+ let current = self . path . fill_type ( ) ;
169+ self . path . set_fill_type ( fill_rule) ;
170+ self . surface
171+ . canvas ( )
172+ . clip_path ( self . path . path ( ) , Some ( ClipOp :: Intersect ) , Some ( true ) ) ;
173+ self . path . set_fill_type_raw ( current) ;
174+ }
175+ }
91176 }
92177
93178 pub fn point_in_path ( & self , path : Option < & Path > , x : f32 , y : f32 , rule : FillRule ) -> bool {
94179 let path = path. unwrap_or ( & self . path ) ;
95180
96181 let total_matrix = self . state . matrix ;
97- let invertible = is_invertible ( & total_matrix) ;
98- if !invertible {
182+ if !is_invertible ( & total_matrix) {
99183 return false ;
100184 }
101185 if !x. is_finite ( ) || !y. is_finite ( ) {
102186 return false ;
103187 }
104- let matrix = total_matrix. clone ( ) ;
105- let inverse = matrix. invert ( ) . unwrap ( ) ;
106- let point: Point = ( x, y) . into ( ) ;
107- let transformed_point = inverse. map_point ( point) ;
108- let mut path_to_compare = path. path ( ) . clone ( ) ;
109- path_to_compare. set_fill_type ( rule. to_fill_type ( ) ) ;
110- path_to_compare. contains ( transformed_point)
188+ let inverse = total_matrix. invert ( ) . unwrap ( ) ;
189+ let transformed_point = inverse. map_point ( Point :: new ( x, y) ) ;
190+ let target_fill = rule. to_fill_type ( ) ;
191+ let current_fill = path. path ( ) . fill_type ( ) ;
192+ if current_fill == target_fill {
193+ path. path ( ) . contains ( transformed_point)
194+ } else {
195+ let mut path_to_compare = path. path ( ) . clone ( ) ;
196+ path_to_compare. set_fill_type ( target_fill) ;
197+ path_to_compare. contains ( transformed_point)
198+ }
111199 }
112200
113201 pub fn point_in_stroke ( & self , path : Option < & Path > , x : f32 , y : f32 ) -> bool {
114202 let path = path. unwrap_or ( & self . path ) ;
115203 let matrix = self . state . matrix ;
116- let invertible = is_invertible ( & matrix) ;
117- if !invertible {
204+ if !is_invertible ( & matrix) {
118205 return false ;
119206 }
120207 if !x. is_finite ( ) || !y. is_finite ( ) {
121208 return false ;
122209 }
123210 let inverse = matrix. invert ( ) . unwrap ( ) ;
124- let point: Point = ( x, y) . into ( ) ;
125- let transformed_point = inverse. map_point ( point) ;
126- let path_to_compare = path. path ( ) ;
127- path_to_compare. contains ( transformed_point)
211+ let transformed_point = inverse. map_point ( Point :: new ( x, y) ) ;
212+ path. path ( ) . contains ( transformed_point)
128213 }
129214}
130215
@@ -134,5 +219,5 @@ fn det(matrix: &Matrix) -> f32 {
134219}
135220
136221fn is_invertible ( matrix : & Matrix ) -> bool {
137- return det ( matrix) != 0.0 ;
222+ det ( matrix) != 0.0
138223}
0 commit comments