From c026abb7de45c7804a2e7b2f4bd16bb562098e52 Mon Sep 17 00:00:00 2001 From: John Tur Date: Sun, 8 Mar 2026 21:56:04 -0400 Subject: [PATCH] Initial attempts --- crates/editor/src/editor.rs | 7 +- crates/editor/src/element.rs | 95 ++++++++++++--- crates/gpui/src/elements/text.rs | 8 +- crates/gpui/src/style.rs | 60 +--------- crates/gpui/src/window.rs | 194 +++++++++++++++++++++++-------- 5 files changed, 235 insertions(+), 129 deletions(-) diff --git a/crates/editor/src/editor.rs b/crates/editor/src/editor.rs index ead4f97ee351246f4d00f4275c4a736c7ffa4926..bce51e8fa208c5c0e3b7b5f42efea95d7944f890 100644 --- a/crates/editor/src/editor.rs +++ b/crates/editor/src/editor.rs @@ -25260,7 +25260,9 @@ impl Editor { window: &mut Window, cx: &mut App, ) -> Option> { - let line_height = self.style(cx).text.line_height_in_pixels(window.rem_size()); + let line_height = window.round_to_nearest_device_pixel( + self.style(cx).text.line_height_in_pixels(window.rem_size()), + ); let text_layout_details = self.text_layout_details(window, cx); let scroll_top = text_layout_details .scroll_anchor @@ -25313,7 +25315,8 @@ impl Editor { let style = &text_layout_details.editor_style; let font_id = window.text_system().resolve_font(&style.text.font()); let font_size = style.text.font_size.to_pixels(window.rem_size()); - let line_height = style.text.line_height_in_pixels(window.rem_size()); + let line_height = window + .round_to_nearest_device_pixel(style.text.line_height_in_pixels(window.rem_size())); let em_width = window.text_system().em_width(font_id, font_size).unwrap(); let em_advance = window.text_system().em_advance(font_id, font_size).unwrap(); diff --git a/crates/editor/src/element.rs b/crates/editor/src/element.rs index 159aee456a6894824ff8e3e212281074498df3c6..66281fc80c8374a241481a95b4cfd86e8ccbf4c9 100644 --- a/crates/editor/src/element.rs +++ b/crates/editor/src/element.rs @@ -2193,7 +2193,7 @@ impl EditorElement { let rem_size = self.rem_size(cx).unwrap_or(window.rem_size()); let mut text_style = self.style.text.clone(); text_style.font_size = font_size; - text_style.line_height_in_pixels(rem_size) + rounded_line_height(window, text_style.line_height_in_pixels(rem_size)) } fn get_minimap_width( @@ -5899,7 +5899,7 @@ impl EditorElement { fn paint_background(&self, layout: &EditorLayout, window: &mut Window, cx: &mut App) { window.paint_layer(layout.hitbox.bounds, |window| { - let scroll_top = layout.position_map.snapshot.scroll_position().y; + let scroll_top = layout.position_map.scroll_position.y; let gutter_bg = cx.theme().colors().editor_gutter_background; window.paint_quad(fill(layout.gutter_hitbox.bounds, gutter_bg)); window.paint_quad(fill( @@ -8849,7 +8849,10 @@ impl LineWithInvisibles { window, max_width: text_width, }); - let line_height = text_style.line_height_in_pixels(window.rem_size()); + let line_height = rounded_line_height( + window, + text_style.line_height_in_pixels(window.rem_size()), + ); let size = element.layout_as_root( size(available_width, AvailableSpace::Definite(line_height)), window, @@ -9580,7 +9583,10 @@ impl Element for EditorElement { let layout_id = match editor.mode { EditorMode::SingleLine => { let rem_size = window.rem_size(); - let height = self.style.text.line_height_in_pixels(rem_size); + let height = rounded_line_height( + window, + self.style.text.line_height_in_pixels(rem_size), + ); let mut style = Style::default(); style.size.height = height.into(); style.size.width = relative(1.).into(); @@ -9623,8 +9629,10 @@ impl Element for EditorElement { style.size.width = relative(1.).into(); if sizing_behavior == SizingBehavior::SizeByContent { let snapshot = editor.snapshot(window, cx); - let line_height = - self.style.text.line_height_in_pixels(window.rem_size()); + let line_height = rounded_line_height( + window, + self.style.text.line_height_in_pixels(window.rem_size()), + ); let scroll_height = (snapshot.max_point().row().next_row().0 as f32) * line_height; style.size.height = scroll_height.into(); @@ -9677,7 +9685,8 @@ impl Element for EditorElement { let rem_size = window.rem_size(); let font_id = window.text_system().resolve_font(&style.text.font()); let font_size = style.text.font_size.to_pixels(rem_size); - let line_height = style.text.line_height_in_pixels(rem_size); + let line_height = + rounded_line_height(window, style.text.line_height_in_pixels(rem_size)); let em_width = window.text_system().em_width(font_id, font_size).unwrap(); let em_advance = window.text_system().em_advance(font_id, font_size).unwrap(); let em_layout_width = window.text_system().em_layout_width(font_id, font_size); @@ -10338,9 +10347,11 @@ impl Element for EditorElement { let start_buffer_row = MultiBufferRow(start_anchor.to_point(&buffer).row); let end_buffer_row = MultiBufferRow(end_anchor.to_point(&buffer).row); - let preliminary_scroll_pixel_position = point( - scroll_position.x * f64::from(em_layout_width), - scroll_position.y * f64::from(line_height), + let preliminary_scroll_pixel_position = rounded_scroll_pixel_position( + window, + scroll_position, + em_layout_width, + line_height, ); let indent_guides = self.layout_indent_guides( content_origin, @@ -10462,9 +10473,16 @@ impl Element for EditorElement { } }); - let scroll_pixel_position = point( - scroll_position.x * f64::from(em_layout_width), - scroll_position.y * f64::from(line_height), + let scroll_pixel_position = rounded_scroll_pixel_position( + window, + scroll_position, + em_layout_width, + line_height, + ); + let scroll_position = scroll_position_from_rounded_pixels( + scroll_pixel_position, + em_layout_width, + line_height, ); let sticky_headers = if !is_minimap && is_singleton @@ -11937,7 +11955,7 @@ impl PointForPosition { impl PositionMap { pub(crate) fn point_for_position(&self, position: gpui::Point) -> PointForPosition { let text_bounds = self.text_hitbox.bounds; - let scroll_position = self.snapshot.scroll_position(); + let scroll_position = self.scroll_position; let position = position - text_bounds.origin; let y = position.y.max(px(0.)).min(self.size.height); let x = position.x + (scroll_position.x as f32 * self.em_layout_width); @@ -12345,6 +12363,46 @@ pub fn register_action( /// Shared between `prepaint` and `compute_auto_height_layout` to ensure /// both full and auto-height editors compute wrap widths consistently. +#[inline] +fn rounded_line_height(window: &Window, line_height: Pixels) -> Pixels { + window.round_to_nearest_device_pixel(line_height) +} + +#[inline] +fn rounded_scroll_pixel_position( + window: &Window, + scroll_position: gpui::Point, + em_layout_width: Pixels, + line_height: Pixels, +) -> gpui::Point { + let dpi_scale = f64::from(window.scale_factor()); + let snap_to_device_pixels = |value: f64| (value * dpi_scale).round() / dpi_scale; + point( + snap_to_device_pixels(scroll_position.x * f64::from(em_layout_width)), + snap_to_device_pixels(scroll_position.y * f64::from(line_height)), + ) +} + +#[inline] +fn scroll_position_from_rounded_pixels( + scroll_pixel_position: gpui::Point, + em_layout_width: Pixels, + line_height: Pixels, +) -> gpui::Point { + point( + if em_layout_width.is_zero() { + 0.0 + } else { + scroll_pixel_position.x / f64::from(em_layout_width) + }, + if line_height.is_zero() { + 0.0 + } else { + scroll_pixel_position.y / f64::from(line_height) + }, + ) +} + fn calculate_wrap_width( soft_wrap: SoftWrap, editor_width: Pixels, @@ -12384,7 +12442,8 @@ fn compute_auto_height_layout( let style = editor.style.as_ref().unwrap(); let font_id = window.text_system().resolve_font(&style.text.font()); let font_size = style.text.font_size.to_pixels(window.rem_size()); - let line_height = style.text.line_height_in_pixels(window.rem_size()); + let line_height = + rounded_line_height(window, style.text.line_height_in_pixels(window.rem_size())); let em_width = window.text_system().em_width(font_id, font_size).unwrap(); let mut snapshot = editor.snapshot(window, cx); @@ -12505,7 +12564,7 @@ mod tests { let style = editor.update(cx, |editor, cx| editor.style(cx).clone()); let line_height = window .update(cx, |_, window, _| { - style.text.line_height_in_pixels(window.rem_size()) + rounded_line_height(window, style.text.line_height_in_pixels(window.rem_size())) }) .unwrap(); let element = EditorElement::new(&editor, style); @@ -12666,7 +12725,7 @@ mod tests { let style = editor.update(cx, |editor, cx| editor.style(cx).clone()); let line_height = window .update(cx, |_, window, _| { - style.text.line_height_in_pixels(window.rem_size()) + rounded_line_height(window, style.text.line_height_in_pixels(window.rem_size())) }) .unwrap(); let element = EditorElement::new(&editor, style); @@ -12743,7 +12802,7 @@ mod tests { let style = editor.update(cx, |editor, cx| editor.style(cx).clone()); let line_height = window .update(cx, |_, window, _| { - style.text.line_height_in_pixels(window.rem_size()) + rounded_line_height(window, style.text.line_height_in_pixels(window.rem_size())) }) .unwrap(); let element = EditorElement::new(&editor, style); diff --git a/crates/gpui/src/elements/text.rs b/crates/gpui/src/elements/text.rs index ded0f596dcea2f6c992961906503adb6829e885f..9b39bd25ca8b1c59da2629e9759d58dd2dabb232 100644 --- a/crates/gpui/src/elements/text.rs +++ b/crates/gpui/src/elements/text.rs @@ -340,9 +340,11 @@ impl TextLayout { ) -> LayoutId { let text_style = window.text_style(); let font_size = text_style.font_size.to_pixels(window.rem_size()); - let line_height = text_style - .line_height - .to_pixels(font_size.into(), window.rem_size()); + let line_height = window.round_to_nearest_device_pixel( + text_style + .line_height + .to_pixels(font_size.into(), window.rem_size()), + ); let runs = if let Some(runs) = runs { runs diff --git a/crates/gpui/src/style.rs b/crates/gpui/src/style.rs index dda49d990ac525f8b9f14b8a61a9c55c43e58e3b..5fe24f19a4dc9b4091be5a6cb446f9814ff265a0 100644 --- a/crates/gpui/src/style.rs +++ b/crates/gpui/src/style.rs @@ -666,72 +666,16 @@ impl Style { if self.is_border_visible() { let border_widths = self.border_widths.to_pixels(rem_size); - let max_border_width = border_widths.max(); - let max_corner_radius = corner_radii.max(); - let zero_size = Size { - width: Pixels::ZERO, - height: Pixels::ZERO, - }; - - let mut top_bounds = Bounds::from_corners( - bounds.origin, - bounds.top_right() + point(Pixels::ZERO, max_border_width.max(max_corner_radius)), - ); - top_bounds.size = top_bounds.size.max(&zero_size); - let mut bottom_bounds = Bounds::from_corners( - bounds.bottom_left() - point(Pixels::ZERO, max_border_width.max(max_corner_radius)), - bounds.bottom_right(), - ); - bottom_bounds.size = bottom_bounds.size.max(&zero_size); - let mut left_bounds = Bounds::from_corners( - top_bounds.bottom_left(), - bottom_bounds.origin + point(max_border_width, Pixels::ZERO), - ); - left_bounds.size = left_bounds.size.max(&zero_size); - let mut right_bounds = Bounds::from_corners( - top_bounds.bottom_right() - point(max_border_width, Pixels::ZERO), - bottom_bounds.top_right(), - ); - right_bounds.size = right_bounds.size.max(&zero_size); - let mut background = self.border_color.unwrap_or_default(); background.a = 0.; - let quad = quad( + window.paint_quad(quad( bounds, corner_radii, background, border_widths, self.border_color.unwrap_or_default(), self.border_style, - ); - - window.with_content_mask(Some(ContentMask { bounds: top_bounds }), |window| { - window.paint_quad(quad.clone()); - }); - window.with_content_mask( - Some(ContentMask { - bounds: right_bounds, - }), - |window| { - window.paint_quad(quad.clone()); - }, - ); - window.with_content_mask( - Some(ContentMask { - bounds: bottom_bounds, - }), - |window| { - window.paint_quad(quad.clone()); - }, - ); - window.with_content_mask( - Some(ContentMask { - bounds: left_bounds, - }), - |window| { - window.paint_quad(quad); - }, - ); + )); } #[cfg(debug_assertions)] diff --git a/crates/gpui/src/window.rs b/crates/gpui/src/window.rs index 3fcb911d2c58f8968bc6b0c66f26ed2de365dd53..eaa9594e7e23b059a0911dace009f425034de91f 100644 --- a/crates/gpui/src/window.rs +++ b/crates/gpui/src/window.rs @@ -11,13 +11,13 @@ use crate::{ MouseMoveEvent, MouseUpEvent, Path, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput, PlatformInputHandler, PlatformWindow, Point, PolychromeSprite, Priority, PromptButton, PromptLevel, Quad, Render, RenderGlyphParams, RenderImage, RenderImageParams, RenderSvgParams, - Replay, ResizeEdge, SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS_X, SUBPIXEL_VARIANTS_Y, - ScaledPixels, Scene, Shadow, SharedString, Size, StrikethroughStyle, Style, SubpixelSprite, - SubscriberSet, Subscription, SystemWindowTab, SystemWindowTabController, TabStopMap, - TaffyLayoutEngine, Task, TextRenderingMode, TextStyle, TextStyleRefinement, ThermalState, - TransformationMatrix, Underline, UnderlineStyle, WindowAppearance, WindowBackgroundAppearance, - WindowBounds, WindowControls, WindowDecorations, WindowOptions, WindowParams, WindowTextSystem, - point, prelude::*, px, rems, size, transparent_black, + Replay, ResizeEdge, SMOOTH_SVG_SCALE_FACTOR, SUBPIXEL_VARIANTS_X, ScaledPixels, Scene, Shadow, + SharedString, Size, StrikethroughStyle, Style, SubpixelSprite, SubscriberSet, Subscription, + SystemWindowTab, SystemWindowTabController, TabStopMap, TaffyLayoutEngine, Task, + TextRenderingMode, TextStyle, TextStyleRefinement, ThermalState, TransformationMatrix, + Underline, UnderlineStyle, WindowAppearance, WindowBackgroundAppearance, WindowBounds, + WindowControls, WindowDecorations, WindowOptions, WindowParams, WindowTextSystem, point, + prelude::*, px, rems, size, transparent_black, }; use anyhow::{Context as _, Result, anyhow}; use collections::{FxHashMap, FxHashSet}; @@ -2108,7 +2108,94 @@ impl Window { /// The line height associated with the current text style. pub fn line_height(&self) -> Pixels { - self.text_style().line_height_in_pixels(self.rem_size()) + self.round_to_nearest_device_pixel(self.text_style().line_height_in_pixels(self.rem_size())) + } + + /// Rounds a logical size or coordinate to the nearest device pixel for this window. + #[inline] + pub fn round_to_nearest_device_pixel(&self, value: Pixels) -> Pixels { + let scale_factor = self.scale_factor(); + px((value.0 * scale_factor).round() / scale_factor) + } + + #[inline] + fn round_point_to_device_pixels(&self, position: Point) -> Point { + point( + self.round_to_nearest_device_pixel(position.x), + self.round_to_nearest_device_pixel(position.y), + ) + } + + #[inline] + fn bounds_from_device_edges( + &self, + left: f32, + top: f32, + right: f32, + bottom: f32, + ) -> Bounds { + let right = right.max(left); + let bottom = bottom.max(top); + Bounds::from_corners( + point(ScaledPixels(left), ScaledPixels(top)), + point(ScaledPixels(right), ScaledPixels(bottom)), + ) + } + + #[inline] + fn round_bounds_to_device_pixels(&self, bounds: Bounds) -> Bounds { + let scale_factor = self.scale_factor(); + let left = (bounds.left().0 * scale_factor).round(); + let top = (bounds.top().0 * scale_factor).round(); + let right = (bounds.right().0 * scale_factor).round(); + let bottom = (bounds.bottom().0 * scale_factor).round(); + self.bounds_from_device_edges(left, top, right, bottom) + } + + #[inline] + fn outset_bounds_to_device_pixels(&self, bounds: Bounds) -> Bounds { + let scale_factor = self.scale_factor(); + let left = (bounds.left().0 * scale_factor).floor(); + let top = (bounds.top().0 * scale_factor).floor(); + let right = (bounds.right().0 * scale_factor).ceil(); + let bottom = (bounds.bottom().0 * scale_factor).ceil(); + self.bounds_from_device_edges(left, top, right, bottom) + } + + #[inline] + fn outset_content_mask_to_device_pixels( + &self, + content_mask: ContentMask, + ) -> ContentMask { + ContentMask { + bounds: self.outset_bounds_to_device_pixels(content_mask.bounds), + } + } + + #[inline] + fn round_edges_to_device_pixels(&self, edges: Edges) -> Edges { + let scale_factor = self.scale_factor(); + Edges { + top: ScaledPixels((edges.top.0 * scale_factor).round()), + right: ScaledPixels((edges.right.0 * scale_factor).round()), + bottom: ScaledPixels((edges.bottom.0 * scale_factor).round()), + left: ScaledPixels((edges.left.0 * scale_factor).round()), + } + } + + #[inline] + fn round_length_to_device_pixels(&self, value: Pixels) -> ScaledPixels { + ScaledPixels((value.0 * self.scale_factor()).round()) + } + + #[inline] + fn round_nonzero_length_to_device_pixels(&self, value: Pixels) -> ScaledPixels { + let rounded = self.round_length_to_device_pixels(value).0; + if value.is_zero() { + ScaledPixels(0.0) + } else { + ScaledPixels(rounded.max(1.0)) + } } /// Call to prevent the default action of an event. Currently only used to prevent @@ -2693,7 +2780,7 @@ impl Window { return f(self); }; - let abs_offset = self.element_offset() + offset; + let abs_offset = self.round_point_to_device_pixels(self.element_offset() + offset); self.with_absolute_element_offset(abs_offset, f) } @@ -2706,7 +2793,8 @@ impl Window { f: impl FnOnce(&mut Self) -> R, ) -> R { self.invalidator.debug_assert_prepaint(); - self.element_offset_stack.push(offset); + self.element_offset_stack + .push(self.round_point_to_device_pixels(offset)); let result = f(self); self.element_offset_stack.pop(); result @@ -3063,13 +3151,12 @@ impl Window { pub fn paint_layer(&mut self, bounds: Bounds, f: impl FnOnce(&mut Self) -> R) -> R { self.invalidator.debug_assert_paint(); - let scale_factor = self.scale_factor(); let content_mask = self.content_mask(); let clipped_bounds = bounds.intersect(&content_mask.bounds); if !clipped_bounds.is_empty() { self.next_frame .scene - .push_layer(clipped_bounds.scale(scale_factor)); + .push_layer(self.outset_bounds_to_device_pixels(clipped_bounds)); } let result = f(self); @@ -3092,16 +3179,16 @@ impl Window { ) { self.invalidator.debug_assert_paint(); - let scale_factor = self.scale_factor(); let content_mask = self.content_mask(); + let scale_factor = self.scale_factor(); let opacity = self.element_opacity(); for shadow in shadows { let shadow_bounds = (bounds + shadow.offset).dilate(shadow.spread_radius); self.next_frame.scene.insert_primitive(Shadow { order: 0, blur_radius: shadow.blur_radius.scale(scale_factor), - bounds: shadow_bounds.scale(scale_factor), - content_mask: content_mask.scale(scale_factor), + bounds: self.outset_bounds_to_device_pixels(shadow_bounds), + content_mask: self.outset_content_mask_to_device_pixels(content_mask.clone()), corner_radii: corner_radii.scale(scale_factor), color: shadow.color.opacity(opacity), }); @@ -3120,17 +3207,16 @@ impl Window { pub fn paint_quad(&mut self, quad: PaintQuad) { self.invalidator.debug_assert_paint(); - let scale_factor = self.scale_factor(); let content_mask = self.content_mask(); let opacity = self.element_opacity(); self.next_frame.scene.insert_primitive(Quad { order: 0, - bounds: quad.bounds.scale(scale_factor), - content_mask: content_mask.scale(scale_factor), + bounds: self.round_bounds_to_device_pixels(quad.bounds), + content_mask: self.outset_content_mask_to_device_pixels(content_mask), background: quad.background.opacity(opacity), border_color: quad.border_color.opacity(opacity), - corner_radii: quad.corner_radii.scale(scale_factor), - border_widths: quad.border_widths.scale(scale_factor), + corner_radii: quad.corner_radii.scale(self.scale_factor()), + border_widths: self.round_edges_to_device_pixels(quad.border_widths), border_style: quad.border_style, }); } @@ -3170,8 +3256,14 @@ impl Window { style.thickness }; let bounds = Bounds { - origin, - size: size(width, height), + origin: point( + ScaledPixels((origin.x.0 * scale_factor).round()), + ScaledPixels((origin.y.0 * scale_factor).round()), + ), + size: size( + self.round_nonzero_length_to_device_pixels(width), + self.round_nonzero_length_to_device_pixels(height), + ), }; let content_mask = self.content_mask(); let element_opacity = self.element_opacity(); @@ -3179,10 +3271,10 @@ impl Window { self.next_frame.scene.insert_primitive(Underline { order: 0, pad: 0, - bounds: bounds.scale(scale_factor), - content_mask: content_mask.scale(scale_factor), + bounds, + content_mask: self.outset_content_mask_to_device_pixels(content_mask), color: style.color.unwrap_or_default().opacity(element_opacity), - thickness: style.thickness.scale(scale_factor), + thickness: self.round_nonzero_length_to_device_pixels(style.thickness), wavy: if style.wavy { 1 } else { 0 }, }); } @@ -3201,8 +3293,14 @@ impl Window { let scale_factor = self.scale_factor(); let height = style.thickness; let bounds = Bounds { - origin, - size: size(width, height), + origin: point( + ScaledPixels((origin.x.0 * scale_factor).round()), + ScaledPixels((origin.y.0 * scale_factor).round()), + ), + size: size( + self.round_nonzero_length_to_device_pixels(width), + self.round_nonzero_length_to_device_pixels(height), + ), }; let content_mask = self.content_mask(); let opacity = self.element_opacity(); @@ -3210,9 +3308,9 @@ impl Window { self.next_frame.scene.insert_primitive(Underline { order: 0, pad: 0, - bounds: bounds.scale(scale_factor), - content_mask: content_mask.scale(scale_factor), - thickness: style.thickness.scale(scale_factor), + bounds, + content_mask: self.outset_content_mask_to_device_pixels(content_mask), + thickness: self.round_nonzero_length_to_device_pixels(style.thickness), color: style.color.unwrap_or_default().opacity(opacity), wavy: 0, }); @@ -3242,7 +3340,10 @@ impl Window { let subpixel_variant = Point { x: (glyph_origin.x.0.fract() * SUBPIXEL_VARIANTS_X as f32).floor() as u8, - y: (glyph_origin.y.0.fract() * SUBPIXEL_VARIANTS_Y as f32).floor() as u8, + // Keep vertical glyph rasterization stable while scrolling. Y-position is + // snapped at quad placement time, so varying Y subpixel glyph variants only + // introduces frame-to-frame wobble. + y: 0, }; let subpixel_rendering = self.should_use_subpixel_rendering(font_id, font_size); let params = RenderGlyphParams { @@ -3265,10 +3366,11 @@ impl Window { })? .expect("Callback above only errors or returns Some"); let bounds = Bounds { - origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into), + origin: point(glyph_origin.x.floor(), glyph_origin.y.round()) + + raster_bounds.origin.map(Into::into), size: tile.bounds.size.map(Into::into), }; - let content_mask = self.content_mask().scale(scale_factor); + let content_mask = self.outset_content_mask_to_device_pixels(self.content_mask()); if subpixel_rendering { self.next_frame.scene.insert_primitive(SubpixelSprite { @@ -3355,10 +3457,11 @@ impl Window { .expect("Callback above only errors or returns Some"); let bounds = Bounds { - origin: glyph_origin.map(|px| px.floor()) + raster_bounds.origin.map(Into::into), + origin: point(glyph_origin.x.floor(), glyph_origin.y.round()) + + raster_bounds.origin.map(Into::into), size: tile.bounds.size.map(Into::into), }; - let content_mask = self.content_mask().scale(scale_factor); + let content_mask = self.outset_content_mask_to_device_pixels(self.content_mask()); let opacity = self.element_opacity(); self.next_frame.scene.insert_primitive(PolychromeSprite { @@ -3390,9 +3493,8 @@ impl Window { self.invalidator.debug_assert_paint(); let element_opacity = self.element_opacity(); - let scale_factor = self.scale_factor(); - let bounds = bounds.scale(scale_factor); + let bounds = self.round_bounds_to_device_pixels(bounds); let params = RenderSvgParams { path, size: bounds.size.map(|pixels| { @@ -3412,7 +3514,7 @@ impl Window { else { return Ok(()); }; - let content_mask = self.content_mask().scale(scale_factor); + let content_mask = self.outset_content_mask_to_device_pixels(self.content_mask()); let svg_bounds = Bounds { origin: bounds.center() - Point::new( @@ -3454,8 +3556,7 @@ impl Window { ) -> Result<()> { self.invalidator.debug_assert_paint(); - let scale_factor = self.scale_factor(); - let bounds = bounds.scale(scale_factor); + let bounds = self.outset_bounds_to_device_pixels(bounds); let params = RenderImageParams { image_id: data.id, frame_index, @@ -3473,17 +3574,15 @@ impl Window { ))) })? .expect("Callback above only returns Some"); - let content_mask = self.content_mask().scale(scale_factor); - let corner_radii = corner_radii.scale(scale_factor); + let content_mask = self.outset_content_mask_to_device_pixels(self.content_mask()); + let corner_radii = corner_radii.scale(self.scale_factor()); let opacity = self.element_opacity(); self.next_frame.scene.insert_primitive(PolychromeSprite { order: 0, pad: 0, grayscale, - bounds: bounds - .map_origin(|origin| origin.floor()) - .map_size(|size| size.ceil()), + bounds, content_mask, corner_radii, tile, @@ -3501,9 +3600,8 @@ impl Window { self.invalidator.debug_assert_paint(); - let scale_factor = self.scale_factor(); - let bounds = bounds.scale(scale_factor); - let content_mask = self.content_mask().scale(scale_factor); + let bounds = self.round_bounds_to_device_pixels(bounds); + let content_mask = self.outset_content_mask_to_device_pixels(self.content_mask()); self.next_frame.scene.insert_primitive(PaintSurface { order: 0, bounds,