Skip to content

Commit

Permalink
Make the ends of vline/hline sharper (#5676)
Browse files Browse the repository at this point in the history
TL;DR: line caps are annoying in two ways:

A) we only add them for lines wider than a pixel
B) they always make the line longer (if added)
  • Loading branch information
emilk authored Feb 4, 2025
1 parent c90b97f commit c6bda9a
Show file tree
Hide file tree
Showing 49 changed files with 135 additions and 97 deletions.
4 changes: 2 additions & 2 deletions crates/egui_demo_app/tests/snapshots/clock.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_app/tests/snapshots/easymarkeditor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Bézier Curve.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Code Editor.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Code Example.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Context Menus.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Drag and Drop.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Extra Viewport.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Font Book.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Frame.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Highlighting.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Misc Demos.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Modals.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Multi Touch.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Painting.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Panels.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Scene.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Screenshot.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Scrolling.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Sliders.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Strip.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Table.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Text Layout.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/TextEdit.png
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Tooltips.png
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Undo Redo.png
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/demos/Window Options.png
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/modals_1.png
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/modals_2.png
4 changes: 2 additions & 2 deletions crates/egui_demo_lib/tests/snapshots/modals_3.png
4 changes: 2 additions & 2 deletions crates/egui_kittest/tests/snapshots/test_shrink.png
40 changes: 39 additions & 1 deletion crates/epaint/src/tessellator.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1002,7 +1002,11 @@ fn stroke_and_fill_path(
let color_outer = Color32::TRANSPARENT;
let color_middle = &stroke.color;

let thin_line = stroke.width <= feathering;
// We add a bit of an epsilon here, because when we round to pixels,
// we can get rounding errors (unless pixels_per_point is an integer).
// And it's better to err on the side of the nicer rendering with line caps
// (the thin-line optimization has no line caps).
let thin_line = stroke.width <= 0.9 * feathering;
if thin_line {
// If the stroke is painted smaller than the pixel width (=feathering width),
// then we risk severe aliasing.
Expand All @@ -1017,6 +1021,8 @@ fn stroke_and_fill_path(
}
}

// TODO(emilk): add line caps (if this is an open line).

let opacity = stroke.width / feathering;

/*
Expand Down Expand Up @@ -1129,6 +1135,10 @@ fn stroke_and_fill_path(

// (in the future it would be great with an option to add a circular end instead)

// TODO(emilk): we should probably shrink before adding the line caps,
// so that we don't add to the area of the line.
// TODO(emilk): make line caps optional.

out.reserve_triangles(6 * n as usize + 4);
out.reserve_vertices(4 * n as usize);

Expand Down Expand Up @@ -1637,20 +1647,48 @@ impl Tessellator {
}

if self.options.round_line_segments_to_pixels {
let feathering = self.feathering;
let pixels_per_point = self.pixels_per_point;

let quarter_pixel = 0.25 * feathering; // Used to avoid fence post problem.

let [a, b] = &mut points;
if a.x == b.x {
// Vertical line
let mut x = a.x;
round_line_segment(&mut x, &stroke, self.pixels_per_point);
a.x = x;
b.x = x;

// Often the ends of the line are exactly on a pixel boundary,
// but we extend line segments with a cap that is a pixel wide…
// Solution: first shrink the line segment (on each end),
// then round to pixel center!
// We shrink by half-a-pixel n total (a quarter on each end),
// so that on average we avoid the fence-post-problem after rounding.
if a.y < b.y {
a.y = (a.y + quarter_pixel).round_to_pixel_center(pixels_per_point);
b.y = (b.y - quarter_pixel).round_to_pixel_center(pixels_per_point);
} else {
a.y = (a.y - quarter_pixel).round_to_pixel_center(pixels_per_point);
b.y = (b.y + quarter_pixel).round_to_pixel_center(pixels_per_point);
}
}
if a.y == b.y {
// Horizontal line
let mut y = a.y;
round_line_segment(&mut y, &stroke, self.pixels_per_point);
a.y = y;
b.y = y;

// See earlier comment for vertical lines
if a.x < b.x {
a.x = (a.x + quarter_pixel).round_to_pixel_center(pixels_per_point);
b.x = (b.x - quarter_pixel).round_to_pixel_center(pixels_per_point);
} else {
a.x = (a.x - quarter_pixel).round_to_pixel_center(pixels_per_point);
b.x = (b.x + quarter_pixel).round_to_pixel_center(pixels_per_point);
}
}
}

Expand Down

0 comments on commit c6bda9a

Please sign in to comment.