@@ -12,18 +12,16 @@ use core::f64::consts::{FRAC_PI_2, PI, TAU};
1212use glam:: { DAffine2 , DVec2 } ;
1313use graphene_std:: Color ;
1414use graphene_std:: math:: quad:: Quad ;
15- use graphene_std:: raster:: curve;
1615use graphene_std:: subpath:: Subpath ;
17- use graphene_std:: transform:: Transform ;
1816use graphene_std:: vector:: click_target:: ClickTargetType ;
1917use graphene_std:: vector:: misc:: { dvec2_to_point, point_to_dvec2} ;
20- use graphene_std:: vector:: stroke:: DashLengthsInput ;
2118use graphene_std:: vector:: style:: { PaintOrder , Stroke , StrokeAlign } ;
2219use graphene_std:: vector:: { PointId , SegmentId , Vector } ;
20+ use js_sys:: { Array , Reflect } ;
2321use kurbo:: { self , Affine , CubicBez , ParamCurve , PathSeg } ;
2422use std:: collections:: HashMap ;
2523use wasm_bindgen:: { JsCast , JsValue } ;
26- use web_sys:: { DomMatrix , OffscreenCanvas , OffscreenCanvasRenderingContext2d , SvgMatrix } ;
24+ use web_sys:: { CanvasPattern , DomMatrix , OffscreenCanvas , OffscreenCanvasRenderingContext2d } ;
2725
2826pub type OverlayProvider = fn ( OverlayContext ) -> Message ;
2927
@@ -910,18 +908,9 @@ impl OverlayContext {
910908 self . end_dpi_aware_transform ( ) ;
911909 }
912910
913- pub fn draw_path_from_subpaths ( & mut self , subpaths : impl Iterator < Item = impl Borrow < Subpath < PointId > > > , stroke_transform : DAffine2 ) {
914- // self.render_context.save();
915- // self.start_dpi_aware_transform();
916-
917- // let a = transform.matrix2.x_axis.x;
918- // let b = transform.matrix2.y_axis.x;
919- // let c = transform.matrix2.x_axis.y;
920- // let d = transform.matrix2.y_axis.y;
921- // let e = transform.translation.x;
922- // let f = transform.translation.y;
923- // self.render_context.transform(a, b, c, d, e, f);
924- // self.render_context.set_transform(a, b, c, d, e, f);
911+ pub fn draw_path_from_subpaths ( & mut self , subpaths : impl Iterator < Item = impl Borrow < Subpath < PointId > > > , stroke_transform : DAffine2 ) -> bool {
912+ // Subpaths on a layer is considered "closed" only if all subpaths are closed.
913+ let mut is_closed = true ;
925914 self . render_context . begin_path ( ) ;
926915 for subpath in subpaths {
927916 let subpath = subpath. borrow ( ) . clone ( ) ;
@@ -964,11 +953,11 @@ impl OverlayContext {
964953
965954 if subpath. closed ( ) {
966955 self . render_context . close_path ( ) ;
956+ } else {
957+ is_closed = false ;
967958 }
968959 }
969-
970- // self.end_dpi_aware_transform();
971- // self.render_context.restore();
960+ return is_closed;
972961 }
973962
974963 /// Used by the Select tool to outline a path or a free point when selected or hovered.
@@ -998,7 +987,7 @@ impl OverlayContext {
998987 }
999988
1000989 /// Default canvas pattern used for filling stroke or fill of a path.
1001- fn fill_canvas_pattern ( & self , color : & Color , transform : DAffine2 ) -> web_sys:: CanvasPattern {
990+ fn fill_canvas_pattern ( & self , color : & Color ) -> web_sys:: CanvasPattern {
1002991 const PATTERN_WIDTH : usize = 4 ;
1003992 const PATTERN_HEIGHT : usize = 4 ;
1004993
@@ -1010,8 +999,6 @@ impl OverlayContext {
1010999 . expect ( "Failed to get canvas context" )
10111000 . dyn_into ( )
10121001 . expect ( "Context should be a canvas 2d context" ) ;
1013- // let [a, b, c, d, e, f] = DAffine2::from_scale(zoom).inverse().to_cols_array();
1014- // pattern_context.set_transform(a, b, c, d, e, f);
10151002
10161003 // 4x4 pixels, 4 components (RGBA) per pixel
10171004 let mut data = [ 0_u8 ; 4 * PATTERN_WIDTH * PATTERN_HEIGHT ] ;
@@ -1031,12 +1018,15 @@ impl OverlayContext {
10311018 pattern_context. put_image_data ( & image_data, 0. , 0. ) . unwrap ( ) ;
10321019
10331020 let pattern = self . render_context . create_pattern_with_offscreen_canvas ( & pattern_canvas, "repeat" ) . unwrap ( ) . unwrap ( ) ;
1034- // let ctx_matrix = (|| {
1035- // let dom_matrix = self.render_context.get_transform().unwrap();
1036- // dom_matrix.
1037- // SvgMatrix::from(dom_matrix.to_json())
1038- // })();
1039- // pattern.set_transform(&ctx_matrix);
1021+ let dom_matrix = self . render_context . get_transform ( ) . unwrap ( ) . inverse ( ) ;
1022+ let set_pattern_transform = |pattern : & CanvasPattern , matrix : & DomMatrix | {
1023+ // Get the JS function: pattern.setTransform
1024+ let func = Reflect :: get ( pattern, & JsValue :: from_str ( "setTransform" ) ) ?;
1025+ // Pass it the matrix
1026+ Reflect :: apply ( & func. into ( ) , pattern, & Array :: of1 ( matrix) ) ?;
1027+ Ok :: < ( ) , JsValue > ( ( ) )
1028+ } ;
1029+ set_pattern_transform ( & pattern, & dom_matrix) ;
10401030
10411031 return pattern;
10421032 }
@@ -1067,12 +1057,12 @@ impl OverlayContext {
10671057 let element_transform = if has_real_stroke { layer_to_viewport * stroke. transform . inverse ( ) } else { DAffine2 :: IDENTITY } ;
10681058
10691059 let [ a, b, c, d, e, f] = element_transform. to_cols_array ( ) ;
1070- self . render_context . transform ( a, b, c, d, e, f) ;
1060+ self . render_context . transform ( a, b, c, d, e, f) . expect ( "element_transform should be set to render stroke properly" ) ;
10711061
1072- self . draw_path_from_subpaths ( subpaths, applied_stroke_transform) ;
1062+ let is_closed = self . draw_path_from_subpaths ( subpaths, applied_stroke_transform) ;
10731063
10741064 let do_fill = || {
1075- self . render_context . set_fill_style_canvas_pattern ( & self . fill_canvas_pattern ( color, element_transform ) ) ;
1065+ self . render_context . set_fill_style_canvas_pattern ( & self . fill_canvas_pattern ( color) ) ;
10761066 self . render_context . fill ( ) ;
10771067 } ;
10781068 let do_stroke = |stroke_weight : f64 | {
@@ -1085,28 +1075,42 @@ impl OverlayContext {
10851075 . set_global_composite_operation ( composite_operation)
10861076 . expect ( "Failed to set global composite operation" ) ;
10871077 } ;
1088- match ( stroke. align , stroke. paint_order ) {
1089- ( StrokeAlign :: Inside , PaintOrder :: StrokeAbove ) => {
1090- do_fill ( ) ;
1091- composite_mode ( "destination-out" ) ;
1092- do_stroke ( stroke. weight ( ) * 2. ) ;
1093- }
1094- ( StrokeAlign :: Inside , PaintOrder :: StrokeBelow ) => do_fill ( ) ,
1095- ( StrokeAlign :: Center , PaintOrder :: StrokeAbove ) => {
1096- do_fill ( ) ;
1097- composite_mode ( "destination-out" ) ;
1098- do_stroke ( stroke. weight ( ) ) ;
1099- }
1100- ( StrokeAlign :: Center , PaintOrder :: StrokeBelow ) => {
1101- do_fill ( ) ;
1078+
1079+ if is_closed {
1080+ match ( stroke. align , stroke. paint_order ) {
1081+ ( StrokeAlign :: Inside , PaintOrder :: StrokeAbove ) => {
1082+ do_fill ( ) ;
1083+ composite_mode ( "destination-out" ) ;
1084+ do_stroke ( stroke. weight ( ) * 2. ) ;
1085+ }
1086+ ( StrokeAlign :: Inside , PaintOrder :: StrokeBelow ) => do_fill ( ) ,
1087+ ( StrokeAlign :: Center , PaintOrder :: StrokeAbove ) => {
1088+ do_fill ( ) ;
1089+ composite_mode ( "destination-out" ) ;
1090+ do_stroke ( stroke. weight ( ) ) ;
1091+ }
1092+ ( StrokeAlign :: Center , PaintOrder :: StrokeBelow ) => {
1093+ do_fill ( ) ;
1094+ }
1095+ // Paint order does not affect this
1096+ ( StrokeAlign :: Outside , _) => {
1097+ do_fill ( ) ;
1098+ }
11021099 }
1103- // Paint order does not affect this
1104- ( StrokeAlign :: Outside , _) => {
1105- do_fill ( ) ;
1100+ } else {
1101+ match stroke. paint_order {
1102+ PaintOrder :: StrokeAbove => {
1103+ do_fill ( ) ;
1104+ composite_mode ( "destination-out" ) ;
1105+ do_stroke ( stroke. weight ( ) ) ;
1106+ }
1107+ PaintOrder :: StrokeBelow => {
1108+ do_fill ( ) ;
1109+ }
11061110 }
11071111 }
11081112 } else {
1109- self . render_context . set_fill_style_canvas_pattern ( & self . fill_canvas_pattern ( color, DAffine2 :: IDENTITY ) ) ;
1113+ self . render_context . set_fill_style_canvas_pattern ( & self . fill_canvas_pattern ( color) ) ;
11101114 self . render_context . fill ( ) ;
11111115 }
11121116
@@ -1132,12 +1136,12 @@ impl OverlayContext {
11321136 let element_transform = if has_real_stroke { layer_to_viewport * stroke. transform . inverse ( ) } else { DAffine2 :: IDENTITY } ;
11331137
11341138 let [ a, b, c, d, e, f] = element_transform. to_cols_array ( ) ;
1135- self . render_context . transform ( a, b, c, d, e, f) ;
1139+ self . render_context . transform ( a, b, c, d, e, f) . expect ( "element_transform should be set to render stroke properly" ) ;
11361140
1137- self . draw_path_from_subpaths ( subpaths, applied_stroke_transform) ;
1141+ let is_closed = self . draw_path_from_subpaths ( subpaths, applied_stroke_transform) ;
11381142
11391143 let do_stroke = |stroke_weight : f64 | {
1140- self . render_context . set_stroke_style_canvas_pattern ( & self . fill_canvas_pattern ( color, element_transform ) ) ;
1144+ self . render_context . set_stroke_style_canvas_pattern ( & self . fill_canvas_pattern ( color) ) ;
11411145 self . render_context . set_line_width ( stroke_weight) ;
11421146 self . render_context . set_line_cap ( stroke. cap . html_canvas_name ( ) . as_str ( ) ) ;
11431147 self . render_context . set_line_join ( stroke. join . html_canvas_name ( ) . as_str ( ) ) ;
@@ -1153,27 +1157,40 @@ impl OverlayContext {
11531157 . set_global_composite_operation ( composite_operation)
11541158 . expect ( "Failed to set global composite operation" ) ;
11551159 } ;
1156- match ( stroke. align , stroke. paint_order ) {
1157- ( StrokeAlign :: Inside , PaintOrder :: StrokeAbove ) => {
1158- // TODO: Use something aside from destination-in
1159- do_stroke ( stroke. weight ( ) * 2. ) ;
1160- composite_mode ( "destination-in" ) ;
1161- do_fill ( ) ;
1162- }
1163- ( StrokeAlign :: Inside , PaintOrder :: StrokeBelow ) => { }
1164- ( StrokeAlign :: Center , PaintOrder :: StrokeAbove ) => {
1165- do_stroke ( stroke. weight ( ) ) ;
1166- }
1167- ( StrokeAlign :: Center , PaintOrder :: StrokeBelow ) => {
1168- do_stroke ( stroke. weight ( ) ) ;
1169- composite_mode ( "destination-out" ) ;
1170- do_fill ( ) ;
1160+
1161+ if is_closed {
1162+ match ( stroke. align , stroke. paint_order ) {
1163+ ( StrokeAlign :: Inside , PaintOrder :: StrokeAbove ) => {
1164+ // Clips away the stroke lying outside the path drawn from the subpaths
1165+ self . render_context . clip ( ) ;
1166+ do_stroke ( stroke. weight ( ) * 2. ) ;
1167+ }
1168+ ( StrokeAlign :: Inside , PaintOrder :: StrokeBelow ) => { }
1169+ ( StrokeAlign :: Center , PaintOrder :: StrokeAbove ) => {
1170+ do_stroke ( stroke. weight ( ) ) ;
1171+ }
1172+ ( StrokeAlign :: Center , PaintOrder :: StrokeBelow ) => {
1173+ do_stroke ( stroke. weight ( ) ) ;
1174+ composite_mode ( "destination-out" ) ;
1175+ do_fill ( ) ;
1176+ }
1177+ // Paint order does not affect this
1178+ ( StrokeAlign :: Outside , _) => {
1179+ do_stroke ( stroke. weight ( ) * 2. ) ;
1180+ composite_mode ( "destination-out" ) ;
1181+ do_fill ( ) ;
1182+ }
11711183 }
1172- // Paint order does not affect this
1173- ( StrokeAlign :: Outside , _) => {
1174- do_stroke ( stroke. weight ( ) * 2. ) ;
1175- composite_mode ( "destination-out" ) ;
1176- do_fill ( ) ;
1184+ } else {
1185+ match stroke. paint_order {
1186+ PaintOrder :: StrokeAbove => {
1187+ do_stroke ( stroke. weight ( ) ) ;
1188+ }
1189+ PaintOrder :: StrokeBelow => {
1190+ do_stroke ( stroke. weight ( ) ) ;
1191+ composite_mode ( "destination-out" ) ;
1192+ do_fill ( ) ;
1193+ }
11771194 }
11781195 }
11791196 }
0 commit comments