py-sdl / py-sdl2

Python ctypes wrapper around SDL2
Other
296 stars 49 forks source link

How to convert SDL_Surface to pygame.Surface? #250

Closed MestreLion closed 1 year ago

MestreLion commented 1 year ago

This is actually a question, not a feature request, but I'm willing to create a PR for docs and/or new method if anyone finds this useful:

Currently I'm planning to use py-sdl2 for the sole purpose of loading SVG files into Pygame, as pygame.image.load() now does accept SVG files but it lacks width, height arguments to choose the rendering size, so it renders only to nominal size (similar to sdl2.sdlimage.IMG_LoadSVG_RW), which is kind of useless.

That said, I believe sdl2.ext.image.load_svg() is the proper tool for the job. But... how can I convert the object returned to a pygame.Surface? I guess the two are very similar, so how to go from one to the other?

I believe it would be something like:

def render_vector(path: str, width: int, height: int) -> pygame.Surface:
    sdlsurf = sdl2.ext.load_svg(file, width, height)
    buf = ...  # how to get a buffer of pixels from sdlsurf?
    format = ...  # "RGBA"?
    pysurf = pygame.image.frombuffer(buf, (width, height), format)
    ...  # should I free any structure? any cleanup code required?
    return pysurf.convert_alpha()  # is convert_alpha() really necessary in this case?

Any help is greatly appreciated.

If needed, I can send a PR to add this "recipe" to one of pysdl2's tutorials or create an ext wrapper for it.

a-hurst commented 1 year ago

Hi @MestreLion, do you know if Pygame can read in images from Numpy arrays? If so, you should be able to use sdl2.ext.surface_to_ndarray to convert it to a Numpy array, then load that into a Pygame surface (either directly or by casting the array to bytes).

No clue if it's possible without the additional Numpy dependency, but the source for sdl2.ext.surface_to_ndarray might give you some ideas!

MestreLion commented 1 year ago

@a-hurst , thanks for the reply! Yes, there are ways to get pygame to create a surface out of a numpy array (either with pygame.image.frombuffer() or pygame.surfarray, tho the latter, surprisingly, does not take an RGBA one!

In any case, although I eventually managed to get pygame to read the output of sdl2.ext.surface_to_ndarray, I have to give up on the whole SDL2 approach. It seems SVG features supported by SDL_image are still in its infant stages: it wasn't able to properly render any of the card themes I'm using, some of them were barely rendered at all. (performance is impressive tho!)

So I kind of had to go back to the old (and awkward) method of LibRsvg + Pycairo (+ Pillow). None of the other SVG renders I tried, such as cairosvg, svglib, etc, were able to render all elements in all card themes. (and their performance is abysmal anyway)

a-hurst commented 1 year ago

@MestreLion Ah yep, SDL_image uses nanoSVG as a backend, which is small/simple/portable but is definitely a bit limited when it comes to full-fat SVG libraries. My own testing found that older and simpler SVGs were generally okay, but newer and more complex ones could break dramatically.

Glad you found something that works, though I hope something better for Python SVG rendering comes along!