Skip to content
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

DrawPolygon with a thickness of less that 0.5 fills produces a filled polygon instead of a outline #323

Closed
tocsoft opened this issue Mar 8, 2024 Discussed in #322 · 3 comments · Fixed by #324
Closed
Labels
bug Something isn't working

Comments

@tocsoft
Copy link
Member

tocsoft commented Mar 8, 2024

  using var image = new Image<Rgba32>(300, 300, Color.White);
  image.Mutate(x => x.DrawPolygon(Color.RebeccaPurple, 0.4f, new PointF[] {
      new(5, 5),
      new(5, 150),
      new(150, 150),
  }));

  image.Save("path.png");

issue kicks in on thicknesses less than 0.5f.

Expected output

image

Actual output

image

Discussed in #322

Originally posted by woutware March 8, 2024
Edit: after further investigations this seems to be a bug, I've edited the text below.

When argument thickness is less than 0.5 (e.g. 0.4), method DrawPolygon now fills the polygon, while it used to only stroke the outline, how to get the old behavior? Tested on ImageSharp 3.1.3, ImageSharp.Drawing 2.1.2. When the thickness is equal to 0.5 or greater, the behavior is like it used to be (I think the expected/correct behavior).

I've narrowed it down to the change having happened in ImageSharp.Drawing 2.0.0. It was OK in 1.0.0.

The call that used to not fill the polygon (from DrawPolygonExtensions):

 public static IImageProcessingContext DrawPolygon(
     this IImageProcessingContext source,
     Color color,
     float thickness,
     params PointF[] points) =>
     source.DrawPolygon(new SolidBrush(color), thickness, points)
@tocsoft tocsoft added the bug Something isn't working label Mar 8, 2024
@tocsoft
Copy link
Member Author

tocsoft commented Mar 8, 2024

this is an issue introduced when we upgraded to Clipper2 as part of 2.0.0

In the 1.0.0 time frame we where working with an int only clipper, in that version we scaled and clipped the polygon to an int based coordinates system before outlining and reversing it again to got back the original scale (for every operation adding plenty of unwanted work), in Clipper2 it moved to a float based system internally so we stopped scaling... I assume there was always a limit in the clipper for both versions (not checked) we just will have just hidden it better in the old version. (we scaled by factors of 1000 or so if memory served).

There is 2nd issues that exacerbates this problem. In cases where the outlining returns no paths (because the clipper has decided it was such a small outline that it would skip it) we currently just return the original (unmodified) path back... which I think is also wrong as no shape is better in this case than a filled polygon.

I think the way to tackle this edge case is, in cases where the thickness is less that 0.5 we scale the path (and thickness) until its above the thickness would be above 0.5, outline as normal, then scale back down after the fact by the same amount. Secondly we should stop returning the original outline in cases where end up with an empty path. I feel the combination of both those should fix this edge case and mitigate any other related cases we have yet to find.

@tocsoft
Copy link
Member Author

tocsoft commented Mar 8, 2024

this is the offending line https://github.com/SixLabors/ImageSharp.Drawing/blob/2a9ee515/src/ImageSharp.Drawing/Shapes/PolygonClipper/PolygonOffsetter.cs#L121 in the third party code

@woutware
Copy link
Contributor

woutware commented Mar 8, 2024

I have been searching and testing all afternoon to find out what triggered it, and it triggered between 0.49 and 0.5, so it didn't seem a coincidence. Nice to see I was onto something indeed, it seems a hairy issue.

It would also be nice to have some guidelines for the limitations of the polygon drawing, like until what thickness do you want to quasi guarantee correct output? At some point no output would probably be better than a full fill, e.g. a thickness of 0.001 would be acceptable as not displayed at all in a bitmap.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
2 participants