1"""Cython memoryview implementation of buffer-api format handler"""
2from ctypes import c_void_p
3from OpenGL_accelerate.formathandler cimport FormatHandler
4import traceback, weakref
5from OpenGL.error import CopyError
6from OpenGL._bytes import bytes,unicode
7
8# Following is from the old python_buffer.pxd
9cdef extern from "Python.h":
10
11    cdef enum:
12        PyBUF_SIMPLE,
13        PyBUF_WRITABLE,
14        PyBUF_WRITEABLE, # backwards compatability
15        PyBUF_FORMAT,
16        PyBUF_ND,
17        PyBUF_STRIDES,
18        PyBUF_C_CONTIGUOUS,
19        PyBUF_F_CONTIGUOUS,
20        PyBUF_ANY_CONTIGUOUS,
21        PyBUF_INDIRECT,
22        PyBUF_CONTIG,
23        PyBUF_CONTIG_RO,
24        PyBUF_STRIDED,
25        PyBUF_STRIDED_RO,
26        PyBUF_RECORDS,
27        PyBUF_RECORDS_RO,
28        PyBUF_FULL,
29        PyBUF_FULL_RO,
30        PyBUF_READ,
31        PyBUF_WRITE,
32        PyBUF_SHADOW
33
34    bint PyObject_CheckBuffer(object obj)
35
36    int PyObject_GetBuffer(object obj, Py_buffer *view, int flags) except -1
37    void PyBuffer_Release(object obj, object view)
38    void* PyBuffer_GetPointer(Py_buffer *view, Py_ssize_t *indices)
39    int PyObject_CopyToObject(object obj, void *buf, Py_ssize_t len, char fortran) except -1
40    bint PyBuffer_IsContiguous(Py_buffer *view, char fort)
41
42    object PyMemoryView_FromObject(object obj)
43    object PyMemoryView_FromBuffer(Py_buffer *view)
44
45    object PyMemoryView_GetContiguous(object obj, int buffertype, char order)
46
47    bint PyMemoryView_Check(object obj)
48    Py_buffer *PyMemoryView_GET_BUFFER(object obj)
49
50cdef class MemoryviewHandler(FormatHandler):
51    cdef public dict array_to_gl_constant
52    cdef public dict gl_constant_to_array
53    isOutput = True
54
55    def __init__( self, ERROR_ON_COPY=None, a_to_gl=None ):
56        if ERROR_ON_COPY is None:
57            from OpenGL import _configflags
58            ERROR_ON_COPY = _configflags.ERROR_ON_COPY
59        if a_to_gl is None:
60            try:
61                from OpenGL.arrays._arrayconstants import ARRAY_TO_GL_TYPE_MAPPING
62            except ImportError as err:
63                raise RuntimeError( "Unable to load array constants" )
64            a_to_gl = ARRAY_TO_GL_TYPE_MAPPING
65        self.ERROR_ON_COPY = ERROR_ON_COPY
66        self.array_to_gl_constant = a_to_gl
67
68    cdef object c_as_memoryview( self, object instance ):
69        """Ensure instance is a memory view instance or make it one"""
70        if not PyMemoryView_Check( instance ):
71            # TODO: respect no-copy flag!
72            instance = PyMemoryView_GetContiguous(
73                instance,
74                PyBUF_STRIDES|PyBUF_FORMAT|PyBUF_C_CONTIGUOUS,
75                'C'
76            )
77        return instance
78    cdef c_from_param( self, object instance, object typeCode ):
79        """simple function-based from_param"""
80        if not PyMemoryView_Check( instance ):
81            raise TypeError( '''Need a MemoryView instance in from_param (call asArray first)''' )
82        return c_void_p( self.c_dataPointer( instance ) )
83    cdef c_dataPointer( self, object instance ):
84        """Retrieve data-pointer directly"""
85        return <size_t> PyMemoryView_GET_BUFFER( self.c_as_memoryview( instance )).buf
86    cdef c_zeros( self, object dims, object typeCode ):
87        """Create an array initialized to zeros"""
88        raise NotImplementedError( "Need to use a subclass to get zeros support" )
89    cdef c_arraySize( self, object instance, object typeCode ):
90        """Retrieve array size reference"""
91        cdef Py_buffer * buffer
92        buffer = PyMemoryView_GET_BUFFER( self.c_as_memoryview( instance ) )
93        return buffer.len//buffer.itemsize
94    cdef c_arrayByteCount( self, object instance ):
95        """Given a data-value, calculate number of bytes required to represent"""
96        return PyMemoryView_GET_BUFFER( self.c_as_memoryview( instance ) ).len
97
98    cdef c_arrayToGLType( self, object instance ):
99        """Given a value, guess OpenGL type of the corresponding pointer"""
100        cdef Py_buffer * buffer
101        buffer = PyMemoryView_GET_BUFFER( self.c_as_memoryview( instance ) )
102        cdef object constant = self.array_to_gl_constant.get( buffer.format )
103        if constant is None:
104            if isinstance(buffer.format,bytes):
105                constant = self.array_to_gl_constant.get(buffer.format.decode('utf-8'))
106        if constant is None:
107            raise TypeError(
108                """Don't know GL type for array of type %r, known types: %s\nvalue:%s"""%(
109                    buffer.format, self.array_to_gl_constant.keys(), buffer.format,
110                )
111            )
112        return constant
113
114    cdef c_asArray( self, object instance, object typeCode ):
115        """Retrieve the given value as a (contiguous) array of type typeCode"""
116        return self.c_as_memoryview( instance )
117#        cdef Py_buffer * buffer
118#        cdef object result
119#        result = self.c_as_memoryview( instance )
120#        buffer = PyMemoryView_GET_BUFFER( result )
121#        actual_type = self.array_to_gl_constant.get( buffer.format, self.array_to_gl_constant )
122#        if actual_type != typeCode:
123#            raise TypeError( 'Incompatible type: %s, needed %s', buffer.format, typeCode )
124#        return result
125    cdef c_unitSize( self, object instance, typeCode ):
126        """Retrieve last dimension of the array"""
127        cdef Py_buffer * buffer
128        view = self.c_as_memoryview( instance )
129        buffer = PyMemoryView_GET_BUFFER( view )
130        return buffer.shape[buffer.ndim-1]
131    cdef c_dimensions( self, object instance ):
132        """Retrieve full set of dimensions for the array as tuple"""
133        cdef Py_buffer * buffer
134        buffer = PyMemoryView_GET_BUFFER( self.c_as_memoryview( instance ) )
135        cdef dim = 0
136        cdef list result
137        result = []
138        for dim in range(buffer.ndim):
139            result.append( buffer.shape[dim] )
140        return result
141