1"""Helper functions for wrapping array-using operations
2
3These are functions intended to be used in wrapping
4GL functions that deal with OpenGL array data-types.
5"""
6import OpenGL
7import ctypes
8from OpenGL import _configflags
9from OpenGL import contextdata, error, converters
10from OpenGL.arrays import arraydatatype
11from OpenGL._bytes import bytes,unicode
12import logging
13_log = logging.getLogger( 'OpenGL.arrays.arrayhelpers' )
14from OpenGL import acceleratesupport
15AsArrayTypedSizeChecked = None
16if acceleratesupport.ACCELERATE_AVAILABLE:
17    try:
18        from OpenGL_accelerate.arraydatatype import AsArrayTypedSizeChecked
19        from OpenGL_accelerate.wrapper import returnPyArgumentIndex
20        from OpenGL_accelerate.arraydatatype import (
21            AsArrayOfType,AsArrayTyped,AsArrayTypedSize
22        )
23    except ImportError as err:
24        _log.warn(
25            "Unable to load arrayhelpers accelerator from OpenGL_accelerate"
26        )
27if AsArrayTypedSizeChecked is None:
28    def returnPointer( result,baseOperation,pyArgs,cArgs, ):
29        """Return the converted object as result of function
30
31        Note: this is a hack that always returns pyArgs[0]!
32        """
33        return pyArgs[0]
34    class AsArrayOfType( converters.PyConverter ):
35        """Given arrayName and typeName coerce arrayName to array of type typeName
36
37        TODO: It should be possible to drop this if ERROR_ON_COPY,
38        as array inputs always have to be the final objects in that
39        case.
40        """
41        argNames = ( 'arrayName','typeName' )
42        indexLookups = (
43            ('arrayIndex', 'arrayName','pyArgIndex'),
44            ('typeIndex', 'typeName','pyArgIndex'),
45        )
46        def __init__( self, arrayName='pointer', typeName='type' ):
47            self.arrayName = arrayName
48            self.typeName = typeName
49        def __call__( self, arg, wrappedOperation, args):
50            """Get the arg as an array of the appropriate type"""
51            type = args[ self.typeIndex ]
52            arrayType = arraydatatype.GL_CONSTANT_TO_ARRAY_TYPE[ type ]
53            return arrayType.asArray( arg )
54    class AsArrayTyped( converters.PyConverter ):
55        """Given arrayName and arrayType, convert arrayName to array of type
56
57        TODO: It should be possible to drop this if ERROR_ON_COPY,
58        as array inputs always have to be the final objects in that
59        case.
60        """
61        argNames = ( 'arrayName','arrayType' )
62        indexLookups = (
63            ('arrayIndex', 'arrayName','pyArgIndex'),
64        )
65        def __init__( self, arrayName='pointer', arrayType=None ):
66            self.arrayName = arrayName
67            self.arrayType = arrayType
68        def __call__( self, arg, wrappedOperation, args):
69            """Get the arg as an array of the appropriate type"""
70            return self.arrayType.asArray( arg )
71    class AsArrayTypedSize( converters.CConverter ):
72        """Given arrayName and arrayType, determine size of arrayName
73        """
74        argNames = ( 'arrayName','arrayType' )
75        indexLookups = (
76            ('arrayIndex', 'arrayName','pyArgIndex'),
77        )
78        def __init__( self, arrayName='pointer', arrayType=None ):
79            self.arrayName = arrayName
80            self.arrayType = arrayType
81        def __call__( self, pyArgs, index, wrappedOperation ):
82            """Get the arg as an array of the appropriate type"""
83            return self.arrayType.arraySize( pyArgs[self.arrayIndex ] )
84else:
85    returnPointer = returnPyArgumentIndex( 0 )
86
87if not _configflags.ERROR_ON_COPY:
88    def asArrayType( typ, size=None ):
89        """Create PyConverter to get first argument as array of type"""
90        return converters.CallFuncPyConverter( typ.asArray )
91else:
92    def asArrayType( typ, size=None ):
93        """No converter required"""
94        return None
95
96if not _configflags.ARRAY_SIZE_CHECKING:
97    asArrayTypeSize = asArrayType
98else:
99    if AsArrayTypedSizeChecked:
100        asArrayTypeSize = AsArrayTypedSizeChecked
101    else:
102        def asArrayTypeSize( typ, size ):
103            """Create PyConverter function to get array as type and check size
104
105            Produces a raw function, not a PyConverter instance
106            """
107            asArray = typ.asArray
108            dataType = typ.typeConstant
109            arraySize = typ.arraySize
110            expectedBytes = ctypes.sizeof( typ.baseType ) * size
111            def asArraySize( incoming, function, args ):
112                handler = typ.getHandler( incoming )
113                result = handler.asArray( incoming, dataType )
114                # check that the number of bytes expected is present...
115                byteSize = handler.arrayByteCount( result )
116                if byteSize != expectedBytes:
117                    raise ValueError(
118                        """Expected %r byte array, got %r byte array"""%(
119                            expectedBytes,
120                            byteSize,
121                        ),
122                        incoming,
123                    )
124                return result
125            return asArraySize
126
127
128if not _configflags.ERROR_ON_COPY:
129    def asVoidArray( ):
130        """Create PyConverter returning incoming as an array of any type"""
131        from OpenGL.arrays import ArrayDatatype
132        return converters.CallFuncPyConverter( ArrayDatatype.asArray )
133else:
134    def asVoidArray( ):
135        """If there's no copying allowed, we can use default passing"""
136        return None
137
138class storePointerType( object ):
139    """Store named pointer value in context indexed by constant
140
141    pointerName -- named pointer argument
142    constant -- constant used to index in the context storage
143
144    Note: OpenGL.STORE_POINTERS can be set with ERROR_ON_COPY
145    to ignore this storage operation.
146
147    Stores the pyArgs (i.e. result of pyConverters) for the named
148    pointer argument...
149    """
150    def __init__( self, pointerName, constant ):
151        self.pointerName = pointerName
152        self.constant = constant
153    def finalise( self, wrapper ):
154        self.pointerIndex = wrapper.pyArgIndex( self.pointerName )
155    def __call__( self, result, baseOperation, pyArgs, cArgs ):
156        contextdata.setValue( self.constant, pyArgs[self.pointerIndex] )
157
158
159def setInputArraySizeType( baseOperation, size, type, argName=0 ):
160    """Decorate function with vector-handling code for a single argument
161
162    if OpenGL.ERROR_ON_COPY is False, then we return the
163    named argument, converting to the passed array type,
164    optionally checking that the array matches size.
165
166    if OpenGL.ERROR_ON_COPY is True, then we will dramatically
167    simplify this function, only wrapping if size is True, i.e.
168    only wrapping if we intend to do a size check on the array.
169    """
170    from OpenGL import wrapper
171    return wrapper.wrapper( baseOperation ).setInputArraySize( argName, size )
172
173def arraySizeOfFirstType( typ, default ):
174    unitSize = typ.unitSize
175    def arraySizeOfFirst( pyArgs, index, baseOperation ):
176        """Return the array size of the first argument"""
177        array = pyArgs[0]
178        if array is None:
179            return default
180        else:
181            return unitSize( array )
182    return arraySizeOfFirst
183