Skip to content

Draw borders with the same color in single bezpath.#366

Merged
nicoburns merged 5 commits intoDioxusLabs:mainfrom
AustinMReppert:fix/border-artifacts
Mar 6, 2026
Merged

Draw borders with the same color in single bezpath.#366
nicoburns merged 5 commits intoDioxusLabs:mainfrom
AustinMReppert:fix/border-artifacts

Conversation

@AustinMReppert
Copy link
Contributor

@AustinMReppert AustinMReppert commented Mar 6, 2026

This is a small fix to ensure adjacent borders with the same color are drawn in a single bezpath to avoid artifacts.

Gemini was used to assist with the code.

Fixes #359

prior:

Of tests run:
201 tests CRASHED (0.77% of run; 0.31% of found)
12020 tests PASSED (45.93% of run; 18.48% of found)
13950 tests FAILED (53.30% of run; 21.45% of found)

with border change:

Of tests run:
201 tests CRASHED (0.77% of run; 0.31% of found)
12130 tests PASSED (46.35% of run; 18.65% of found)
13840 tests FAILED (52.88% of run; 21.28% of found)

Prior:
Screenshot from 2026-03-05 23-42-42

With border Fix:

Screenshot from 2026-03-05 23-43-34

@AustinMReppert AustinMReppert marked this pull request as draft March 6, 2026 06:14
@AustinMReppert AustinMReppert marked this pull request as ready for review March 6, 2026 08:17
@AustinMReppert AustinMReppert changed the title Draw adjacent borders with the same color in single bezpath. Draw borders with the same color in single bezpath. Mar 6, 2026
Comment on lines +853 to +870
let has_multiple_edges =
next_border_index < count && borders[next_border_index].0 == color;
if has_multiple_edges {
let mut border_path = borders[start_border_index].1.take().unwrap();
while next_border_index < count && borders[next_border_index].0 == color {
border_path.extend(&borders[next_border_index].1.take().unwrap());
next_border_index += 1;
}
scene.fill(Fill::NonZero, self.transform, color, None, &border_path);
} else {
scene.fill(
Fill::NonZero,
self.transform,
color,
None,
borders[start_border_index].1.as_ref().unwrap(),
);
}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be simplified:

let mut next_border_index = start_border_index + 1;
let mut border_path = borders[start_border_index].1.take().unwrap();
while next_border_index < count && borders[next_border_index].0 == color {
    border_path.extend(&borders[next_border_index].1.take().unwrap());
    next_border_index += 1;
}
scene.fill(Fill::NonZero, self.transform, color, None, &border_path);

The key insight here being that the has_multiple_edges "if" is using the same condition as the while loop in the first branch of the if. So we simplify to only using a while loop that just does zero iterations in the "else" case.

if has_multiple_edges {
let mut border_path = borders[start_border_index].1.take().unwrap();
while next_border_index < count && borders[next_border_index].0 == color {
border_path.extend(&borders[next_border_index].1.take().unwrap());
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The docs on BezPath::extend (https://docs.rs/kurbo/latest/kurbo/struct.BezPath.html#method.extend) say:

Note that if you’re attempting to make a continuous path, you will generally want to ensure that the iterator does not contain any MoveTo or ClosePath elements. Note especially that many (open) shapes will start with a MoveTo if you use their path_elements function. Some shapes have alternatives for this use case, such as Arc::append_iter.

Which I think we are doing here?

I guess if it works this is fine. But it would be nice to understand if/why this is ok.

let current_color = style.clone_color();

let mut borders: [(Color, Option<BezPath>); 4] = [
(Color::TRANSPARENT, None),
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: It might be nice for readability to use a struct for this? So we have .color and .path rather than .0 and .1...

@nicoburns
Copy link
Collaborator

Gemini was used to assist with the code.

I appreciate the disclosure!

@nicoburns nicoburns merged commit faef685 into DioxusLabs:main Mar 6, 2026
14 checks passed
@AustinMReppert AustinMReppert deleted the fix/border-artifacts branch March 7, 2026 00:57
@behdad
Copy link

behdad commented Mar 7, 2026

Does this have any performance implications? In hb-raster, Claude prototyped a fix for this issue, but the performance degradation was too high (~50%).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Borders Have Artifacts

3 participants