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

SDL_CreateRGBSurfaceWithFormatFrom silently failing when passed a sub-array of a `bytes` object

Open TTimo opened this issue 2 years ago • 1 comments

What doesn't work?

I'm finding that SDL_CreateRGBSurfaceWithFormatFrom either throws or fails to take in the pixel data (but creates a valid empty SDL_Surface) when passed in a sub-array of a bytes object directly. But it works if I setup an intermediate python object first.

How To Reproduce

This test case stems from difficulties I ran into when adding support for setting the application's window icon:

#!/usr/bin/env python

import sys
import sdl2

if __name__ == '__main__':
    sdl2.SDL_Init(sdl2.SDL_INIT_VIDEO)
    window = sdl2.SDL_CreateWindow(
        'test'.encode(),
        sdl2.SDL_WINDOWPOS_CENTERED,
        sdl2.SDL_WINDOWPOS_CENTERED,
        640,
        480,
        0
        )

    assert sys.byteorder == 'little'
    # A dummy 4 bytes header, followed by an little endian ARGB solid green fill
    # For instance, say we loaded a TGA file with 18 bytes worth of header
    w = h= 256
    pixels = b'\xff\xff\xff\xff' b'\x00\xff\x00\xff' * w * h

    # Arch Linux, Python 3.11.7:       pixels don't get through, yields a blank/fully transparent icon
    # Arch Linux, Python 3.12.2 (AUR): pixels don't get through, yields a blank/fully transparent icon
    # Windows, Python 3.11.8:          OSError: exception: access violation reading [..]
    # Windows, Python 3.12.2:          works fine!
    # (all with PySDL2 0.9.16, current atm and SDL 2.30.0)
    icon = sdl2.SDL_CreateRGBSurfaceWithFormatFrom(pixels[4:4+w*h*4], w, h, 32, w*4, sdl2.SDL_PIXELFORMAT_ARGB8888)
    assert icon is not None
    assert icon.contents.format.contents.format == sdl2.SDL_PIXELFORMAT_ARGB8888

    sdl2.SDL_SetWindowIcon(window, icon)

    import time
    time.sleep(4)

    # Workaround: this..
    pixels2 = pixels[4:4+w*h*4]
    icon = sdl2.SDL_CreateRGBSurfaceWithFormatFrom(pixels2, w, h, 32, w*4, sdl2.SDL_PIXELFORMAT_ARGB8888)
    assert icon is not None
    assert icon.contents.format.contents.format == sdl2.SDL_PIXELFORMAT_ARGB8888

    sdl2.SDL_SetWindowIcon(window, icon)

    import time
    time.sleep(4)

On Windows with Python 3.12 the first SDL_CreateRGBSurfaceWithFormatFrom call works. Elsewhere it'll either ignore the pixels and yield a transparent/empty surface, or throw an OSErrror.

The workaround is what bugs me the most .. just .. pixels2 = pixels[4:4+w*h*4] and all platforms work.

Platform:

  • OS: Windows 11 64-bit, Arch Linux (have not tested others)
  • Python Version: 3.11.7 3.11.8 3.12.2
  • SDL2 Version: 2.0.30
  • Using pysdl2-dll: Yes on Windows

Additional context Add any other context about the problem here.

TTimo avatar Feb 26 '24 20:02 TTimo

For a bit more context, this is the initial in-situ investigation of this problem. I only found the workaround later on when writing a test case: https://gitlab.steamos.cloud/devkit/steamos-devkit/-/commit/7e89557f692b7a1437ebbf4f51464a95d4f8f52f#f42379bd6d31600aba6eff88f0c3311df57ccea9

TTimo avatar Feb 26 '24 21:02 TTimo