@@ -14,15 +14,16 @@ use graphene_std::Color;
1414use graphene_std:: math:: quad:: Quad ;
1515use graphene_std:: raster:: curve;
1616use graphene_std:: subpath:: Subpath ;
17+ use graphene_std:: transform:: Transform ;
1718use graphene_std:: vector:: click_target:: ClickTargetType ;
1819use graphene_std:: vector:: misc:: { dvec2_to_point, point_to_dvec2} ;
1920use graphene_std:: vector:: stroke:: DashLengthsInput ;
20- use graphene_std:: vector:: style:: { PaintOrder , Stroke } ;
21+ use graphene_std:: vector:: style:: { PaintOrder , Stroke , StrokeAlign } ;
2122use graphene_std:: vector:: { PointId , SegmentId , Vector } ;
2223use kurbo:: { self , Affine , CubicBez , ParamCurve , PathSeg } ;
2324use std:: collections:: HashMap ;
2425use wasm_bindgen:: { JsCast , JsValue } ;
25- use web_sys:: { OffscreenCanvas , OffscreenCanvasRenderingContext2d } ;
26+ use web_sys:: { DomMatrix , OffscreenCanvas , OffscreenCanvasRenderingContext2d , SvgMatrix } ;
2627
2728pub type OverlayProvider = fn ( OverlayContext ) -> Message ;
2829
@@ -997,7 +998,7 @@ impl OverlayContext {
997998 }
998999
9991000 /// Default canvas pattern used for filling stroke or fill of a path.
1000- fn fill_canvas_pattern ( & self , color : & Color ) -> web_sys:: CanvasPattern {
1001+ fn fill_canvas_pattern ( & self , color : & Color , transform : DAffine2 ) -> web_sys:: CanvasPattern {
10011002 const PATTERN_WIDTH : usize = 4 ;
10021003 const PATTERN_HEIGHT : usize = 4 ;
10031004
@@ -1009,6 +1010,8 @@ impl OverlayContext {
10091010 . expect ( "Failed to get canvas context" )
10101011 . dyn_into ( )
10111012 . 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);
10121015
10131016 // 4x4 pixels, 4 components (RGBA) per pixel
10141017 let mut data = [ 0_u8 ; 4 * PATTERN_WIDTH * PATTERN_HEIGHT ] ;
@@ -1026,7 +1029,16 @@ impl OverlayContext {
10261029
10271030 let image_data = web_sys:: ImageData :: new_with_u8_clamped_array_and_sh ( wasm_bindgen:: Clamped ( & data) , PATTERN_WIDTH as u32 , PATTERN_HEIGHT as u32 ) . unwrap ( ) ;
10281031 pattern_context. put_image_data ( & image_data, 0. , 0. ) . unwrap ( ) ;
1029- return self . render_context . create_pattern_with_offscreen_canvas ( & pattern_canvas, "repeat" ) . unwrap ( ) . unwrap ( ) ;
1032+
1033+ 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);
1040+
1041+ return pattern;
10301042 }
10311043
10321044 /// Fills the area inside the path (with an optional pattern). Assumes `color` is in gamma space.
@@ -1059,31 +1071,51 @@ impl OverlayContext {
10591071
10601072 self . draw_path_from_subpaths ( subpaths, applied_stroke_transform) ;
10611073
1062- match stroke. paint_order {
1063- PaintOrder :: StrokeAbove => {
1064- self . render_context . set_fill_style_canvas_pattern ( & self . fill_canvas_pattern ( color) ) ;
1065- self . render_context . fill ( ) ;
1066-
1067- // Make the stroke transparent and erase the fill area overlapping the stroke.
1068- self . render_context . set_stroke_style_str ( & "#000000" ) ;
1069- self . render_context . set_line_width ( stroke. weight ( ) ) ;
1070- self . render_context . set_global_composite_operation ( "destination-out" ) . expect ( "Failed to set global composite operation" ) ;
1071- self . render_context . stroke ( ) ;
1074+ let do_fill = || {
1075+ self . render_context . set_fill_style_canvas_pattern ( & self . fill_canvas_pattern ( color, element_transform) ) ;
1076+ self . render_context . fill ( ) ;
1077+ } ;
1078+ let do_stroke = |stroke_weight : f64 | {
1079+ self . render_context . set_line_width ( stroke_weight) ;
1080+ self . render_context . set_stroke_style_str ( & "#000000" ) ;
1081+ self . render_context . stroke ( ) ;
1082+ } ;
1083+ let composite_mode = |composite_operation : & str | {
1084+ self . render_context
1085+ . set_global_composite_operation ( composite_operation)
1086+ . expect ( "Failed to set global composite operation" ) ;
1087+ } ;
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 ( ) ) ;
10721099 }
1073- PaintOrder :: StrokeBelow => {
1074- self . render_context . set_fill_style_canvas_pattern ( & self . fill_canvas_pattern ( color) ) ;
1075- self . render_context . fill ( ) ;
1100+ ( StrokeAlign :: Center , PaintOrder :: StrokeBelow ) => {
1101+ do_fill ( ) ;
1102+ }
1103+ // Paint order does not affect this
1104+ ( StrokeAlign :: Outside , _) => {
1105+ do_fill ( ) ;
10761106 }
10771107 }
10781108 } else {
1079- self . render_context . set_fill_style_canvas_pattern ( & self . fill_canvas_pattern ( color) ) ;
1109+ self . render_context . set_fill_style_canvas_pattern ( & self . fill_canvas_pattern ( color, DAffine2 :: IDENTITY ) ) ;
10801110 self . render_context . fill ( ) ;
10811111 }
10821112
10831113 self . end_dpi_aware_transform ( ) ;
10841114 self . render_context . restore ( ) ;
10851115 }
10861116
1117+ // WARN: Don't use source-in, destination-atop, destination-in, copy
1118+ // on the main canvas as it will erase the existing overlays
10871119 pub fn stroke_overlay ( & mut self , subpaths : impl Iterator < Item = impl Borrow < Subpath < PointId > > > , layer_to_viewport : DAffine2 , color : & Color , stroke : Option < Stroke > ) {
10881120 // Render for elements with stroke
10891121 //----StrokeAlign
@@ -1104,21 +1136,44 @@ impl OverlayContext {
11041136
11051137 self . draw_path_from_subpaths ( subpaths, applied_stroke_transform) ;
11061138
1107- self . render_context . set_stroke_style_canvas_pattern ( & self . fill_canvas_pattern ( color) ) ;
1108- self . render_context . set_line_width ( stroke. weight ) ;
1109- self . render_context . set_line_cap ( stroke. cap . html_canvas_name ( ) . as_str ( ) ) ;
1110- self . render_context . set_line_join ( stroke. join . html_canvas_name ( ) . as_str ( ) ) ;
1111- self . render_context . set_miter_limit ( stroke. join_miter_limit ) ;
1112- match stroke. paint_order {
1113- PaintOrder :: StrokeAbove => {
1114- self . render_context . stroke ( ) ;
1139+ let do_stroke = |stroke_weight : f64 | {
1140+ self . render_context . set_stroke_style_canvas_pattern ( & self . fill_canvas_pattern ( color, element_transform) ) ;
1141+ self . render_context . set_line_width ( stroke_weight) ;
1142+ self . render_context . set_line_cap ( stroke. cap . html_canvas_name ( ) . as_str ( ) ) ;
1143+ self . render_context . set_line_join ( stroke. join . html_canvas_name ( ) . as_str ( ) ) ;
1144+ self . render_context . set_miter_limit ( stroke. join_miter_limit ) ;
1145+ self . render_context . stroke ( ) ;
1146+ } ;
1147+ let do_fill = || {
1148+ self . render_context . set_fill_style_str ( & "#000000" ) ;
1149+ self . render_context . fill ( ) ;
1150+ } ;
1151+ let composite_mode = |composite_operation : & str | {
1152+ self . render_context
1153+ . set_global_composite_operation ( composite_operation)
1154+ . expect ( "Failed to set global composite operation" ) ;
1155+ } ;
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 ( ) ;
11151162 }
1116- PaintOrder :: StrokeBelow => {
1117- self . render_context . stroke ( ) ;
1118-
1119- self . render_context . set_fill_style_str ( & "#000000" ) ;
1120- self . render_context . set_global_composite_operation ( "destination-out" ) . expect ( "Failed to set global composite operation" ) ;
1121- self . render_context . fill ( ) ;
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 ( ) ;
1171+ }
1172+ // Paint order does not affect this
1173+ ( StrokeAlign :: Outside , _) => {
1174+ do_stroke ( stroke. weight ( ) * 2. ) ;
1175+ composite_mode ( "destination-out" ) ;
1176+ do_fill ( ) ;
11221177 }
11231178 }
11241179 }
0 commit comments