251 views
Skip to first unread message

Joshua Landau

unread,
Oct 21, 2012, 3:37:28 PM10/21/12
to cython...@googlegroups.com
Say I have this code:
 
import pygame
import numpy
import random 
 
surface = pygame.Surface((10, 10))
surface.fill(0x012345) 
 
cdef unsigned char [:] flat_surface_pixels = surface.get_buffer()
cdef unsigned char [:, :, :] np_surface_pixels = numpy.array(surface.get_buffer()).reshape((10, 10, 4))

for _ in range(10): 
a, b, c = random.randint(0, 9), random.randint(0, 9), random.randint(0, 3)
print(
np_surface_pixels[a, b, c], # Index Numpy's reshaped version
flat_surface_pixels[a*40 + b*4 + c] # "Faked" indexing
)

and this output:

%> python -c "import pyximport; pyximport.install(); import tttst"
warning: tttst.pyx:17:21: Index should be typed for more efficient access
(0, 0)
(69, 69)
<snip> 
(35, 35)
(0, 0)

 ------------------------------

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"?

mark florisson

unread,
Oct 21, 2012, 5:40:01 PM10/21/12
to cython...@googlegroups.com
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.

Joshua Landau

unread,
Oct 21, 2012, 8:04:08 PM10/21/12
to cython...@googlegroups.com
On 21 October 2012 22:40, mark florisson <markflo...@gmail.com> wrote:
On 21 October 2012 20:37, Joshua Landau <joshua.l...@gmail.com> wrote:
> Say I have this code:
<snip> 
> 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!

Joshua Landau

unread,
Oct 21, 2012, 9:18:01 PM10/21/12
to cython...@googlegroups.com
On 22 October 2012 01:04, Joshua Landau <joshua.l...@gmail.com> wrote:
<snip>
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

After much searching, I believe what I've been wanting is:
py_buffer
unsigned char[:] memview_data
unsigned char* data
and
def __init__(self, surface):
self.py_buffer = memoryview(surface.get_buffer())
self.memview_data = self.py_buffer
self.data = &self.memview_data[0]
  
The &-syntax was not obvious to me.
Thank you very much, Mark Florisson,  for your help.
Reply all
Reply to author
Forward
0 new messages