@@ -26,6 +26,12 @@ pub struct OverlayContext {
2626 #[ specta( skip) ]
2727 pub render_context : web_sys:: CanvasRenderingContext2d ,
2828 pub size : DVec2 ,
29+ // The device pixel ratio is a property provided
30+ // by the browser window and is the css pixel size
31+ // divided by the physical pixel size. It allows
32+ // better pixel density of visualizations on
33+ // high dpi displays (such as Retina displays).
34+ pub device_pixel_ratio : f64 ,
2935}
3036// Message hashing isn't used but is required by the message system macros
3137impl core:: hash:: Hash for OverlayContext {
@@ -39,6 +45,7 @@ impl OverlayContext {
3945
4046 pub fn dashed_quad ( & mut self , quad : Quad , color_fill : Option < & str > , dash_width : Option < f64 > , dash_gap_width : Option < f64 > , dash_offset : Option < f64 > ) {
4147 // Set the dash pattern
48+ self . start_dpi_aware_transform ( ) ;
4249 if let Some ( dash_width) = dash_width {
4350 let dash_gap_width = dash_gap_width. unwrap_or ( 1. ) ;
4451 let array = js_sys:: Array :: new ( ) ;
@@ -82,6 +89,7 @@ impl OverlayContext {
8289 if dash_offset. is_some ( ) && dash_offset != Some ( 0. ) {
8390 self . render_context . set_line_dash_offset ( 0. ) ;
8491 }
92+ self . end_dpi_aware_transform ( ) ;
8593 }
8694
8795 pub fn line ( & mut self , start : DVec2 , end : DVec2 , color : Option < & str > ) {
@@ -90,6 +98,7 @@ impl OverlayContext {
9098
9199 pub fn dashed_line ( & mut self , start : DVec2 , end : DVec2 , color : Option < & str > , dash_width : Option < f64 > , dash_gap_width : Option < f64 > , dash_offset : Option < f64 > ) {
92100 // Set the dash pattern
101+ self . start_dpi_aware_transform ( ) ;
93102 if let Some ( dash_width) = dash_width {
94103 let dash_gap_width = dash_gap_width. unwrap_or ( 1. ) ;
95104 let array = js_sys:: Array :: new ( ) ;
@@ -127,9 +136,11 @@ impl OverlayContext {
127136 if dash_offset. is_some ( ) && dash_offset != Some ( 0. ) {
128137 self . render_context . set_line_dash_offset ( 0. ) ;
129138 }
139+ self . end_dpi_aware_transform ( ) ;
130140 }
131141
132142 pub fn manipulator_handle ( & mut self , position : DVec2 , selected : bool , color : Option < & str > ) {
143+ self . start_dpi_aware_transform ( ) ;
133144 let position = position. round ( ) - DVec2 :: splat ( 0.5 ) ;
134145
135146 self . render_context . begin_path ( ) ;
@@ -142,6 +153,7 @@ impl OverlayContext {
142153 self . render_context . set_stroke_style_str ( color. unwrap_or ( COLOR_OVERLAY_BLUE ) ) ;
143154 self . render_context . fill ( ) ;
144155 self . render_context . stroke ( ) ;
156+ self . end_dpi_aware_transform ( ) ;
145157 }
146158
147159 pub fn manipulator_anchor ( & mut self , position : DVec2 , selected : bool , color : Option < & str > ) {
@@ -150,6 +162,25 @@ impl OverlayContext {
150162 self . square ( position, None , Some ( color_fill) , Some ( color_stroke) ) ;
151163 }
152164
165+ /// Transforms the Canvas Context To Adjust for DPI
166+ ///
167+ /// Overwrites all existing tranforms.
168+ /// This operation can be reversed with [`Self::reset_transform`].
169+ fn start_dpi_aware_transform ( & self ) {
170+ let [ a, b, c, d, e, f] = DAffine2 :: from_scale ( DVec2 :: splat ( self . device_pixel_ratio ) ) . to_cols_array ( ) ;
171+ self . render_context
172+ . set_transform ( a, b, c, d, e, f)
173+ . expect ( "transform should be able to be set to be able to account for DPI" ) ;
174+ }
175+
176+ /// Untransforms the Canvas Context To Adjust for DPI
177+ ///
178+ /// Warning: this function doesn't only reset the
179+ /// DPI adjustment, it resets the entire transform.
180+ fn end_dpi_aware_transform ( & self ) {
181+ self . render_context . reset_transform ( ) . expect ( "transform should be able to be reset to be able to account for DPI" ) ;
182+ }
183+
153184 pub fn square ( & mut self , position : DVec2 , size : Option < f64 > , color_fill : Option < & str > , color_stroke : Option < & str > ) {
154185 let size = size. unwrap_or ( MANIPULATOR_GROUP_MARKER_SIZE ) ;
155186 let color_fill = color_fill. unwrap_or ( COLOR_OVERLAY_WHITE ) ;
@@ -158,12 +189,14 @@ impl OverlayContext {
158189 let position = position. round ( ) - DVec2 :: splat ( 0.5 ) ;
159190 let corner = position - DVec2 :: splat ( size) / 2. ;
160191
192+ self . start_dpi_aware_transform ( ) ;
161193 self . render_context . begin_path ( ) ;
162194 self . render_context . rect ( corner. x , corner. y , size, size) ;
163195 self . render_context . set_fill_style_str ( color_fill) ;
164196 self . render_context . set_stroke_style_str ( color_stroke) ;
165197 self . render_context . fill ( ) ;
166198 self . render_context . stroke ( ) ;
199+ self . end_dpi_aware_transform ( ) ;
167200 }
168201
169202 pub fn pixel ( & mut self , position : DVec2 , color : Option < & str > ) {
@@ -173,22 +206,27 @@ impl OverlayContext {
173206 let position = position. round ( ) - DVec2 :: splat ( 0.5 ) ;
174207 let corner = position - DVec2 :: splat ( size) / 2. ;
175208
209+ self . start_dpi_aware_transform ( ) ;
176210 self . render_context . begin_path ( ) ;
177211 self . render_context . rect ( corner. x , corner. y , size, size) ;
178212 self . render_context . set_fill_style_str ( color_fill) ;
179213 self . render_context . fill ( ) ;
214+ self . end_dpi_aware_transform ( ) ;
180215 }
181216
182217 pub fn circle ( & mut self , position : DVec2 , radius : f64 , color_fill : Option < & str > , color_stroke : Option < & str > ) {
183218 let color_fill = color_fill. unwrap_or ( COLOR_OVERLAY_WHITE ) ;
184219 let color_stroke = color_stroke. unwrap_or ( COLOR_OVERLAY_BLUE ) ;
185220 let position = position. round ( ) ;
221+
222+ self . start_dpi_aware_transform ( ) ;
186223 self . render_context . begin_path ( ) ;
187224 self . render_context . arc ( position. x , position. y , radius, 0. , TAU ) . expect ( "Failed to draw the circle" ) ;
188225 self . render_context . set_fill_style_str ( color_fill) ;
189226 self . render_context . set_stroke_style_str ( color_stroke) ;
190227 self . render_context . fill ( ) ;
191228 self . render_context . stroke ( ) ;
229+ self . end_dpi_aware_transform ( ) ;
192230 }
193231
194232 pub fn draw_arc ( & mut self , center : DVec2 , radius : f64 , start_from : f64 , end_at : f64 ) {
@@ -252,6 +290,7 @@ impl OverlayContext {
252290 pub fn pivot ( & mut self , position : DVec2 ) {
253291 let ( x, y) = ( position. round ( ) - DVec2 :: splat ( 0.5 ) ) . into ( ) ;
254292
293+ self . start_dpi_aware_transform ( ) ;
255294 // Circle
256295
257296 self . render_context . begin_path ( ) ;
@@ -276,9 +315,11 @@ impl OverlayContext {
276315 self . render_context . move_to ( x, y - crosshair_radius) ;
277316 self . render_context . line_to ( x, y + crosshair_radius) ;
278317 self . render_context . stroke ( ) ;
318+ self . end_dpi_aware_transform ( ) ;
279319 }
280320
281321 pub fn outline_vector ( & mut self , vector_data : & VectorData , transform : DAffine2 ) {
322+ self . start_dpi_aware_transform ( ) ;
282323 self . render_context . begin_path ( ) ;
283324 let mut last_point = None ;
284325 for ( _, bezier, start_id, end_id) in vector_data. segment_bezier_iter ( ) {
@@ -290,16 +331,20 @@ impl OverlayContext {
290331
291332 self . render_context . set_stroke_style_str ( COLOR_OVERLAY_BLUE ) ;
292333 self . render_context . stroke ( ) ;
334+ self . end_dpi_aware_transform ( ) ;
293335 }
294336
295337 pub fn outline_bezier ( & mut self , bezier : Bezier , transform : DAffine2 ) {
338+ self . start_dpi_aware_transform ( ) ;
296339 self . render_context . begin_path ( ) ;
297340 self . bezier_command ( bezier, transform, true ) ;
298341 self . render_context . set_stroke_style_str ( COLOR_OVERLAY_BLUE ) ;
299342 self . render_context . stroke ( ) ;
343+ self . end_dpi_aware_transform ( ) ;
300344 }
301345
302346 fn bezier_command ( & self , bezier : Bezier , transform : DAffine2 , move_to : bool ) {
347+ self . start_dpi_aware_transform ( ) ;
303348 let Bezier { start, end, handles } = bezier. apply_transformation ( |point| transform. transform_point2 ( point) ) ;
304349 if move_to {
305350 self . render_context . move_to ( start. x , start. y ) ;
@@ -310,9 +355,11 @@ impl OverlayContext {
310355 bezier_rs:: BezierHandles :: Quadratic { handle } => self . render_context . quadratic_curve_to ( handle. x , handle. y , end. x , end. y ) ,
311356 bezier_rs:: BezierHandles :: Cubic { handle_start, handle_end } => self . render_context . bezier_curve_to ( handle_start. x , handle_start. y , handle_end. x , handle_end. y , end. x , end. y ) ,
312357 }
358+ self . end_dpi_aware_transform ( ) ;
313359 }
314360
315361 pub fn outline ( & mut self , subpaths : impl Iterator < Item = impl Borrow < Subpath < PointId > > > , transform : DAffine2 ) {
362+ self . start_dpi_aware_transform ( ) ;
316363 self . render_context . begin_path ( ) ;
317364 for subpath in subpaths {
318365 let subpath = subpath. borrow ( ) ;
@@ -359,6 +406,7 @@ impl OverlayContext {
359406
360407 self . render_context . set_stroke_style_str ( COLOR_OVERLAY_BLUE ) ;
361408 self . render_context . stroke ( ) ;
409+ self . end_dpi_aware_transform ( ) ;
362410 }
363411
364412 pub fn get_width ( & self , text : & str ) -> f64 {
@@ -378,7 +426,7 @@ impl OverlayContext {
378426 Pivot :: End => -padding,
379427 } ;
380428
381- let [ a, b, c, d, e, f] = ( transform * DAffine2 :: from_translation ( DVec2 :: new ( x, y) ) ) . to_cols_array ( ) ;
429+ let [ a, b, c, d, e, f] = ( DAffine2 :: from_scale ( DVec2 :: splat ( self . device_pixel_ratio ) ) * transform * DAffine2 :: from_translation ( DVec2 :: new ( x, y) ) ) . to_cols_array ( ) ;
382430 self . render_context . set_transform ( a, b, c, d, e, f) . expect ( "Failed to rotate the render context to the specified angle" ) ;
383431
384432 if let Some ( background) = background_color {
0 commit comments