py-sdl2 icon indicating copy to clipboard operation
py-sdl2 copied to clipboard

SDL_gfx: Circle radius greater than 1023 results in deformation

Open atomicdad opened this issue 2 years ago • 1 comments

I don't know if this is the correct place to bring up this issue...partially because I don't know the exact cause. It looks like the pysdl2-dll package uses version 1.0.4 of the SDL_gfx library, but I don't know exactly how to confirm this. I say this because the change log at https://www.ferzkopp.net/wordpress/2016/01/02/sdl_gfx-sdl2_gfx/ shows a fix in 1.0.4 for large radius values resulting in an int overflow. I'm not sure if this is related or not.

import sdl2
import sdl2.sdlgfx
import sdl2.ext


def run():

    sdl2.ext.init()
    window = sdl2.ext.Window("circle test", size=(1700, 900))
    window.show()

    if "-hardware" in sys.argv:
        renderflags = sdl2.render.SDL_RENDERER_ACCELERATED | sdl2.render.SDL_RENDERER_PRESENTVSYNC
    else:
        renderflags = sdl2.render.SDL_RENDERER_SOFTWARE
    context = sdl2.ext.Renderer(window, flags=renderflags)

    color = 0xFFFFFFFF
    x, y = -400, 450
    r = 1023
    sdl2.sdlgfx.circleColor(context.sdlrenderer, x, y, r, color)
    x, y = 0, 450
    r = 1024
    sdl2.sdlgfx.circleColor(context.sdlrenderer, x, y, r, color)
    
    context.present()
    running = True
    while running:
        events = sdl2.ext.get_events()
        for event in events:
            if event.type == sdl2.SDL_QUIT:
                running = False
                break
    sdl2.ext.quit()
    return 0


if __name__ == "__main__":
    sys.exit(run())
  • OS: Windows 10 64-bit
  • Python Version: 3.11
  • PySDL2 Version: 0.9.16
  • Using pysdl2-dll: Yes

Here is an image of the result. https://i.stack.imgur.com/Cpmvh.png

atomicdad avatar Oct 24 '23 13:10 atomicdad

@atomicdad This is the right place, thanks for reporting the issue!

pysdl2-dll does indeed bundle the latest version of sdl2_gfx (1.0.4), so I don't think an out-of-date library is the issue. Additionally I double-checked the Python bindings to make sure all the argument types were being bound correctly and those were fine as well. Finally, I looked a quick look at the sdl2_gfx source code and found this, which was added in as part of the patch fixing large-radius circles you mentioned:

	if (rxi >= 512 || ryi >= 512)
	{
		ellipseOverscan = DEFAULT_ELLIPSE_OVERSCAN / 4;
	} 
	else if (rxi >= 256 || ryi >= 256)
	{
		ellipseOverscan = DEFAULT_ELLIPSE_OVERSCAN / 2;
	}
	else
	{
		ellipseOverscan = DEFAULT_ELLIPSE_OVERSCAN / 1;
	}

Given that the overscan value is set to change every time the radius increases by a power of 2 beyond 256, I'm guessing the author never imagined circles with a radius of 1024 being an issue and didn't think to add an if (rxi >= 1024 || ryi >= 1024) statement dividing the default overscan by 8 (and also didn't think of writing a more generalizable patch).

Unfortunately the sdl2_gfx source code hasn't been updated by its author since 2018, and I don't think anyone else has attempted to keep an up-to-date fork, so I don't think the odds are good on this getting fixed. If you're open to using other drawing libraries with SDL2, you can use Pillow's ImageDraw module along with sdl2.ext.pillow_to_surface. It's a more Pythonic API and also supports proper anti-aliasing (unlike gfx). I personally use aggdraw for all my PySDL2 shape rendering, although the API's a bit wonky and ImageDraw probably supports most of the same functionality nowadays so I'd try that first.

Hope this helps!

a-hurst avatar Oct 24 '23 23:10 a-hurst