-
Notifications
You must be signed in to change notification settings - Fork 55
feat(canvas): add scale factor to canvas options #122
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -35,19 +35,41 @@ | |
| const CELL_HEIGHT: f64 = 19.0; | ||
|
|
||
| /// Options for the [`CanvasBackend`]. | ||
| #[derive(Debug, Default)] | ||
| #[derive(Debug)] | ||
| pub struct CanvasBackendOptions { | ||
| /// The element ID. | ||
| grid_id: Option<String>, | ||
| /// Override the automatically detected size. | ||
| size: Option<(u32, u32)>, | ||
| /// Scale factor for the canvas. Setting this to a value greater than 1.0 | ||
| /// will improve the rendering quality on high-DPI displays, at the cost | ||
| /// of performance. | ||
| /// | ||
| /// The memory usage of the canvas will increase with the square of the scale factor. | ||
| /// For example, a scale factor of 2.0 will use 4 times the memory. | ||
| /// | ||
| /// The default value is 1.0. | ||
| /// | ||
| /// Generally, a scale factor of 2.0 is a good compromise between quality and performance. | ||
| scale: f64, | ||
| /// Always clip foreground drawing to the cell rectangle. Helpful when | ||
| /// dealing with out-of-bounds rendering from problematic fonts. Enabling | ||
| /// this option may cause some performance issues when dealing with large | ||
| /// numbers of simultaneous changes. | ||
| always_clip_cells: bool, | ||
| } | ||
|
|
||
| impl Default for CanvasBackendOptions { | ||
| fn default() -> Self { | ||
| Self { | ||
| grid_id: None, | ||
| size: None, | ||
| scale: 1.0, | ||
| always_clip_cells: false, | ||
| } | ||
| } | ||
| } | ||
|
|
||
| impl CanvasBackendOptions { | ||
| /// Constructs a new [`CanvasBackendOptions`]. | ||
| pub fn new() -> Self { | ||
|
|
@@ -65,6 +87,28 @@ | |
| self.size = Some(size); | ||
| self | ||
| } | ||
|
|
||
| /// Sets the scale factor for the canvas. Setting this to a value greater than 1.0 | ||
| /// will improve the rendering quality on high-DPI displays, at the cost | ||
| /// of performance. | ||
| /// | ||
| /// The memory usage of the canvas will increase with the square of the scale factor. | ||
| /// For example, a scale factor of 2.0 will use 4 times the memory. | ||
| /// | ||
| /// The default value is 1.0. | ||
| /// | ||
| /// Generally, a scale factor of 2.0 is a good compromise between quality and performance. | ||
| /// | ||
| /// # Panics | ||
| /// | ||
| /// Panics if `scale` is not positive. | ||
| pub fn scale(mut self, scale: f64) -> Self { | ||
| if scale <= 0.0 { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. should it be i think it would be good to set a restriction the upper bounds too, maybe 4.0 - it's already at x16 memory consumption. i haven't tried it locally, but i guess setting really high values would cause the canvas to fail somehow. otherwise i think it LGTM 👍
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
When the scale is set to 0, the graphic will not be displayed, so it is reasonable to throw an error when scale=0.
Excessively high scaling requires substantial hardware resources, but it is not impossible to run. In such cases, I believe a hard error should not be issued - after all, with sufficient computational resources, it remains feasible to execute. A warning, or even just a note in the documentation to caution users, would suffice.
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
i can't read if statements 😅 yeah, i agree - scale=0 should throw.
do values above 3-4 have any visual impact? what happens to the canvas/tab/browser/system if it allocates more memory than the user has available? (i can take a look after work too)
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
In fact, 3x resolution has reached the limit of what is discernible. Perhaps I should make this point clearer in the documentation.
First, the running speed will slow down. If increased further, the entire webpage (Firefox) or the
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. if there's no discernible difference except resource consumption past 3x, i think we can restrict the range to <= 3 (or 4). or is there ever a reason for higher scale values?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. would be also nice to document the side effects of slightly high values |
||
| panic!("Scale must be greater than 0"); | ||
| } | ||
| self.scale = scale; | ||
Wybxc marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| self | ||
| } | ||
| } | ||
|
|
||
| /// Canvas renderer. | ||
|
|
@@ -84,9 +128,13 @@ | |
| parent_element: web_sys::Element, | ||
| width: u32, | ||
| height: u32, | ||
| scale: f64, | ||
| background_color: Color, | ||
| ) -> Result<Self, Error> { | ||
| let canvas = create_canvas_in_element(&parent_element, width, height)?; | ||
| let inner_width = (width as f64 * scale) as u32; | ||
| let inner_height = (height as f64 * scale) as u32; | ||
| let canvas = create_canvas_in_element(&parent_element, inner_width, inner_height)?; | ||
| canvas.set_attribute("style", &format!("width: {width}px; height: {height}px;"))?; | ||
|
|
||
| let context_options = Map::new(); | ||
| context_options.set(&JsValue::from_str("alpha"), &Boolean::from(JsValue::TRUE)); | ||
|
|
@@ -101,6 +149,7 @@ | |
| .expect("Unable to cast canvas context"); | ||
| context.set_font("16px monospace"); | ||
| context.set_text_baseline("top"); | ||
| context.scale(scale, scale)?; | ||
|
|
||
| Ok(Self { | ||
| inner: canvas, | ||
|
|
@@ -162,7 +211,7 @@ | |
| .size | ||
| .unwrap_or_else(|| (parent.client_width() as u32, parent.client_height() as u32)); | ||
|
|
||
| let canvas = Canvas::new(parent, width, height, Color::Black)?; | ||
| let canvas = Canvas::new(parent, width, height, options.scale, Color::Black)?; | ||
| let buffer = get_sized_buffer_from_canvas(&canvas.inner); | ||
| let changed_cells = bitvec![0; buffer.len() * buffer[0].len()]; | ||
| Ok(Self { | ||
|
|
@@ -273,7 +322,7 @@ | |
| /// 1. Only processes cells that have changed since the last render. | ||
| /// 2. Tracks the last foreground color used to avoid unnecessary style changes | ||
| /// 3. Only creates clipping paths for potentially problematic glyphs (non-ASCII) | ||
| /// or when `always_clip_cells` is enabled. | ||
|
Check warning on line 325 in src/backend/canvas.rs
|
||
| fn draw_symbols(&mut self) -> Result<(), Error> { | ||
| let changed_cells = &self.changed_cells; | ||
| let mut index = 0; | ||
|
|
@@ -405,7 +454,7 @@ | |
| fn draw_debug(&mut self) -> Result<(), Error> { | ||
| self.canvas.context.save(); | ||
|
|
||
| let color = self.debug_mode.as_ref().unwrap(); | ||
|
Check warning on line 457 in src/backend/canvas.rs
|
||
| for (y, line) in self.buffer.iter().enumerate() { | ||
| for (x, _) in line.iter().enumerate() { | ||
| self.canvas.context.set_stroke_style_str(color); | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.