Skip to content

[rtextures] Review ImageDrawCircle() asymmetry - #6118

Merged
raysan5 merged 1 commit into
raysan5:masterfrom
Risu-swift:fix/imagedrawcircle-symmetry
Sep 2, 2026
Merged

[rtextures] Review ImageDrawCircle() asymmetry#6118
raysan5 merged 1 commit into
raysan5:masterfrom
Risu-swift:fix/imagedrawcircle-symmetry

Conversation

@Risu-swift

@Risu-swift Risu-swift commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Fixes #6117

ImageDrawCircle filled 2*radius pixels wide but 2*radius + 1 tall. Each horizontal span started at centerX - x with width 2*x, which covers centerX - x through centerX + x - 1, so the column at centerX + x was never written. The fill came out one column short on the right, half a pixel off-centre, and out of line with ImageDrawCircleLines, which is 2*radius + 1 in both directions.

This widens the four spans by one pixel. The fill becomes (2r+1) x (2r+1), centred on (centerX, centerY), and matches ImageDrawCircleLines exactly.

Verified on master (9f3cadf) with radii 1-6: before the change, 1, 5, 7, 9, 11 and 13 outline pixels fell outside the fill respectively. After it, zero for every radius.

Note this makes every filled circle one pixel wider than before, so existing output shifts. The alternative would be narrowing ImageDrawCircleLines instead, but the outline is the one that is currently correct and centred.

ImageDrawCircle filled 2*radius pixels wide but 2*radius + 1 tall. Each
horizontal span started at centerX - x with width 2*x, which covers
centerX - x through centerX + x - 1, leaving the column at centerX + x
unwritten. The result was one column short on the right, off-centre, and
inconsistent with ImageDrawCircleLines, which is 2*radius + 1 in both
directions.

Widen the four spans by one pixel so the fill is (2r+1) x (2r+1), centred
on (centerX, centerY) and aligned with ImageDrawCircleLines.

Fixes raysan5#6117
@Risu-swift
Risu-swift force-pushed the fix/imagedrawcircle-symmetry branch from 1943a90 to 45bab0c Compare September 2, 2026 02:58
@raysan5

raysan5 commented Sep 2, 2026

Copy link
Copy Markdown
Owner

@Risu-swift please, could you provide comparison screenshots and also comparison with DrawCircle() functions for consistency?

EDIT: Sorry, just saw the open issue. Still, how does it compare to DrawCircle() at different radius?

@raysan5 raysan5 changed the title [rtextures] Fix ImageDrawCircle asymmetry [rtextures] Review ImageDrawCircle() asymmetry Sep 2, 2026
@Risu-swift

Copy link
Copy Markdown
Contributor Author

@Risu-swift please, could you provide comparison screenshots and also comparison with DrawCircle() functions for consistency?

EDIT: Sorry, just saw the open issue. Still, how does it compare to DrawCircle() at different radius?

DrawCircle() works in continuous coordinates, so (cx, cy) sits on a pixel corner and the circle covers [c-r, c+r-1] on both axes: 2r x 2r, symmetric. The Image* functions index pixels, so (cx, cy) is a pixel and symmetry about it needs an odd diameter of 2r+1.

Radius 5, left to right: DrawCircle, ImageDrawCircle before, ImageDrawCircle after. Blue is filled, red is an ImageDrawCircleLines pixel the fill misses, and the yellow line is the centre in each function's own convention: on a pixel edge for DrawCircle, through the middle of a pixel for the image functions.

image

Measured on master (9f3cadf), centre (20,20) on a 40x40 target, alpha > 127 counted as covered:

r DrawCircle ImageDrawCircle before ImageDrawCircle after ImageDrawCircleLines
1 2x2 2x3 3x3 3x3
2 4x4 4x5 5x5 5x5
3 6x6 6x7 7x7 7x7
4 8x8 8x9 9x9 9x9
5 10x10 10x11 11x11 11x11
6 12x12 12x13 13x13 13x13
7 14x14 14x15 15x15 15x15
8 16x16 16x17 17x17 17x17

DrawCircle and ImageDrawCircleLines are each internally consistent. Before this PR ImageDrawCircle was the only one that isn't, being 2r wide but 2r+1 tall. After it, it matches ImageDrawCircleLines, which is the function it gets paired with.

It stays one pixel larger than DrawCircle, but so does ImageDrawCircleLines today. Making the image set match DrawCircle at 2r would mean narrowing ImageDrawCircleLines too and giving up symmetry about the centre pixel. Happy to do it that way instead if you would rather the whole set agree on 2r.

One thing this PR does not change, visible in the figure above: the shapes differ as well as the sizes. The left panel is round, the right is a diamond with clipped corners. DrawCircle rasterizes a 36-gon by pixel-centre coverage, so at this size it matches a true disc exactly. ImageDrawCircle uses the midpoint outline points directly as span endpoints, so each row grows by a fixed two pixels regardless of where the circle actually falls. It is most visible at small radii and converges as the radius grows.

Changing that would mean replacing the span walk with a distance test, which is a much bigger behaviour change than this PR, so I have left it alone. Happy to open a separate issue if it is worth tracking.

@raysan5
raysan5 merged commit 9b2efc4 into raysan5:master Sep 2, 2026
@raysan5

raysan5 commented Sep 2, 2026

Copy link
Copy Markdown
Owner

@Risu-swift thanks for the review. Meging this change for now but definitely behaviour should be the same as DrawCircle() and DrawCircleLines()

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.

[rtextures] ImageDrawCircle() is asymmetric and disagrees with ImageDrawCircleLines()

2 participants