> The problem is that "surface.get_buffer()" returns a 1D buffer when it
> "should" really return a 10x10x4 buffer. I want to reshape it. One way is
> shown in my code: convert it to a Numpy array and use the reshape method.
>
> Another is to imagine it is reshaped by multiplying by what the strides
> "should" be every time I index.
>
> However, the first method requires Numpy and the second isn't pretty or very
> nice. Is there a way of doing this without copying, Numpy or "faking"?
You can use a wrapper extension class that exposes the buffer
interface and compute the 2D shape and strides. See
https://github.com/cython/cython/blob/master/Cython/Utility/MemoryView.pyx#L183
for inspiration.
Thank you immensely. This seems like just the thing (although a little over my head).
I think I understand what you were suggesting. If I do, I'm almost done. The only thing I need now[, I hope,] is a method to turn a buffer into a pointer. Note that my C experience is very limited.
__getbuffer__ needs to set info.data, which needs to be of type char*. The thing I'm trying to wrap is a buffer (BufferProxy, convertable to a memoryview). Is there a non-copying way to do this?
My code looks a lot like this:
cdef class ValidBuffer_SurfaceWrapper:
cdef:
unsigned char *data
Py_ssize_t real_shape[3]
Py_ssize_t real_strides[3]
py_buffer
def __init__(self, surface):
self.py_buffer = surface.get_buffer()
self.data = WEIRD COPYING STUFF HERE
self.real_shape[0], self.real_shape[1] = surface.get_size()
self.real_shape[2] = 4
self.real_strides[0] = 4*self.real_shape[1]
self.real_strides[1] = 4
self.real_strides[2] = 1
def __getbuffer__(self, Py_buffer *info, int flags):
info.buf = self.data
info.len = self.py_buffer.length
info.ndim = 3
info.shape = self.real_shape
info.strides = self.real_strides
info.suboffsets = NULL
info.itemsize = 1
info.readonly = 0
info.format = "B"
info.obj = self
# "TESTS "
import pygame
def change(surface):
buffer_attempt = memoryview(ValidBuffer_SurfaceWrapper(surface))
cdef unsigned char[:, :, :] cdef_buffer_attempt = buffer_attempt
cdef_buffer_attempt[4, 4, 0] = 0xff
surface = pygame.Surface((10, 10))
surface.fill(0xffffffff)
change(surface)
screen = pygame.display.set_mode((10, 10))
screen.blit(surface, (0,0))
pygame.display.flip()
The place I need more help is the section screaming "WEIRD COPYING STUFF HERE".
Again, it needs to be something that does not actually copy anything: I want changes to be reflected in the original surface (my original example should have used numpy.array(..., copy=False) or whatever the command is).
Not being a numpy object there is no obvious .data attribute I can snatch.
Any help is very much appreciated!