Skip to content

Commit

Permalink
Better rounding of rectangles with thin outlines (#5571)
Browse files Browse the repository at this point in the history
Better positioning of rectangle outline when the stroke width is less
than one pixel
  • Loading branch information
emilk authored Jan 2, 2025
1 parent 46b58e5 commit 4784136
Show file tree
Hide file tree
Showing 2 changed files with 18 additions and 38 deletions.
2 changes: 1 addition & 1 deletion crates/egui_kittest/src/snapshot.rs
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ pub enum SnapshotError {
}

const HOW_TO_UPDATE_SCREENSHOTS: &str =
"Run `UPDATE_SNAPSHOTS=1 cargo test` to update the snapshots.";
"Run `UPDATE_SNAPSHOTS=1 cargo test --all-features` to update the snapshots.";

impl Display for SnapshotError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
Expand Down
54 changes: 17 additions & 37 deletions crates/epaint/src/tessellator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1291,29 +1291,6 @@ impl Tessellator {
self.clip_rect = clip_rect;
}

#[inline(always)]
pub fn round_to_pixel(&self, point: f32) -> f32 {
(point * self.pixels_per_point).round() / self.pixels_per_point
}

#[inline(always)]
pub fn round_to_pixel_center(&self, point: f32) -> f32 {
((point * self.pixels_per_point - 0.5).round() + 0.5) / self.pixels_per_point
}

#[inline(always)]
pub fn round_pos_to_pixel(&self, pos: Pos2) -> Pos2 {
pos2(self.round_to_pixel(pos.x), self.round_to_pixel(pos.y))
}

#[inline(always)]
pub fn round_pos_to_pixel_center(&self, pos: Pos2) -> Pos2 {
pos2(
self.round_to_pixel_center(pos.x),
self.round_to_pixel_center(pos.y),
)
}

/// Tessellate a clipped shape into a list of primitives.
pub fn tessellate_clipped_shape(
&mut self,
Expand Down Expand Up @@ -1716,8 +1693,16 @@ impl Tessellator {
// Since the stroke extends outside of the rectangle,
// we can round the rectangle sides to the physical pixel edges,
// and the filled rect will appear crisp, as will the inside of the stroke.
let Stroke { .. } = stroke; // Make sure we remember to update this if we change `stroke` to `PathStroke`
rect = rect.round_to_pixels(self.pixels_per_point);
let Stroke { width, .. } = stroke; // Make sure we remember to update this if we change `stroke` to `PathStroke`
if width <= self.feathering && !stroke.is_empty() {
// If the stroke is thin, make sure its center is in the center of the pixel:
rect = rect
.expand(width / 2.0)
.round_to_pixel_center(self.pixels_per_point)
.shrink(width / 2.0);
} else {
rect = rect.round_to_pixels(self.pixels_per_point);
}
}

// It is common to (sometimes accidentally) create an infinitely sized rectangle.
Expand All @@ -1727,7 +1712,7 @@ impl Tessellator {

let old_feathering = self.feathering;

if old_feathering < blur_width {
if self.feathering < blur_width {
// We accomplish the blur by using a larger-than-normal feathering.
// Feathering is usually used to make the edges of a shape softer for anti-aliasing.

Expand Down Expand Up @@ -1836,10 +1821,7 @@ impl Tessellator {
// The contents of the galley are already snapped to pixel coordinates,
// but we need to make sure the galley ends up on the start of a physical pixel:
let galley_pos = if self.options.round_text_to_pixels {
pos2(
self.round_to_pixel(galley_pos.x),
self.round_to_pixel(galley_pos.y),
)
galley_pos.round_to_pixels(self.pixels_per_point)
} else {
*galley_pos
};
Expand Down Expand Up @@ -1917,13 +1899,11 @@ impl Tessellator {
);

if *underline != Stroke::NONE {
self.scratchpad_path.clear();
self.scratchpad_path.add_line_segment([
self.round_pos_to_pixel_center(row_rect.left_bottom()),
self.round_pos_to_pixel_center(row_rect.right_bottom()),
]);
self.scratchpad_path
.stroke_open(0.0, &PathStroke::from(*underline), out);
self.tessellate_line_segment(
[row_rect.left_bottom(), row_rect.right_bottom()],
*underline,
out,
);
}
}
}
Expand Down

0 comments on commit 4784136

Please sign in to comment.