You are not logged in.

#1 2012-08-04 11:35:17

Goran
Member
Registered: 2012-01-24
Posts: 53

[Solved] Python ctypes: Trying to access SDL memory.

I'm trying to use ctypes to interface with SDL. More specifically, I'm trying to modify the pixel data on the SDL_Surface created by SDL_SetVideoMode, which returns a pointer to that surface.

Well, in Python 3.x (and probably 2.x ... I'm using 3), using ctypes to call SDL_SetVideoMode returns an integer that specifies an address, and that can be turned into a pointer like so:

p_surface = ctypes.cast(addr_surface, ctypes.POINTER(ctypes.c_uint))

# I can now dereference with a proper offset (based on the memory layout of the SDL_Surface structure)
# to read stuff like width and height, which works as expected:

print("Screen width: {:d}".format(p_surface[2]))
print("Screen height: {:d}".format(p_surface[3]))

Now, here's the part that stumps me:

According to the documentation for SDL_Surface, it looks like this:

typedef struct SDL_Surface {
     Uint32 flags;                           /* Read-only */
     SDL_PixelFormat *format;                /* Read-only */
     int w, h;                               /* Read-only */
     Uint16 pitch;                           /* Read-only */
     void *pixels;                           /* Read-write */
     SDL_Rect clip_rect;                     /* Read-only */
     int refcount;                           /* Read-mostly */ 

    /* This structure also contains private fields not shown here */
} SDL_Surface;

So, I figure that the pointer to pixels starts on byte addr_surface + 18 (4 ints at 4 bytes each, and one short at 2 bytes), and that I could use that information to get access to actual pixels:

pp_pixels = ctypes.cast(addr_surface + 18, ctypes.POINTER(ctypes.c_uint))
p_pixels = ctypes.cast(pp_pixels[0], ctypes.POINTER(ctypes.c_char))

p_pixels[0] # should give me the value of first byte for the first pixel, but instead I get a segfault

Anyone have any ideas as to why this is happening? I would appreciate any insight.

Last edited by Goran (2012-08-04 23:58:07)

Offline

#2 2012-08-04 13:17:53

Trent
Member
From: Baltimore, MD (US)
Registered: 2009-04-16
Posts: 987

Re: [Solved] Python ctypes: Trying to access SDL memory.

Goran wrote:

So, I figure that the pointer to pixels starts on byte addr_surface + 18

You can't make any assumptions about the storage of fields inside a struct unless you have documentation to prove it. What's logical isn't good enough.

However, I might imagine that there's padding on that Uint16 to avoid misaligning the pointer on an address that's not a multiple of 4, so you could try addr_surface + 20 instead. But the only way to be sure is to find documentation (or write a test program in C that uses offsetof).

Offline

#3 2012-08-04 23:57:15

Goran
Member
Registered: 2012-01-24
Posts: 53

Re: [Solved] Python ctypes: Trying to access SDL memory.

Seems like you were right. I tried the whole operation with addr_surface + 20, and it worked as expected.

In studying ctypes a little further, I found a much better way:

class SDL_Surface(Structure):
        _fields_ = [
                        ("fields", c_uint32),
                        ("format", c_void_p),
                        ("w", c_int),
                        ("h", c_int),
                        ("pitch", c_uint16),
                        ("pixels", POINTER(c_char))
                ]

sdl.SDL_SetVideoMode.restype = POINTER(SDL_Surface)

p_surface = sdl.SDL_SetVideoMode(128, 128, 24, 0)

surface = p_surface.contents # copies portion of the structure defined in _fields_

surface.pixels[0] # gets the value of first byte in first pixel.

It works really well.

Thanks for your help, Trent.

Last edited by Goran (2012-08-05 00:13:26)

Offline

Board footer

Powered by FluxBB