1 // This contains the support for QOpenGL value arrays.
2 //
3 // Copyright (c) 2021 Riverbank Computing Limited <info@riverbankcomputing.com>
4 //
5 // This file is part of PyQt5.
6 //
7 // This file may be used under the terms of the GNU General Public License
8 // version 3.0 as published by the Free Software Foundation and appearing in
9 // the file LICENSE included in the packaging of this file.  Please review the
10 // following information to ensure the GNU General Public License version 3.0
11 // requirements will be met: http://www.gnu.org/copyleft/gpl.html.
12 //
13 // If you do not wish to use this file under the terms of the GPL version 3.0
14 // then you may purchase a commercial license.  For more information contact
15 // info@riverbankcomputing.com.
16 //
17 // This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
18 // WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
19 
20 
21 #include <Python.h>
22 
23 #include "sipAPIQtGui.h"
24 
25 #if defined(SIP_FEATURE_PyQt_OpenGL)
26 
27 #include <QtGlobal>
28 
29 #if QT_VERSION >= 0x050100
30 
31 #include "qpyopengl_api.h"
32 #include "qpyopengl_data_cache.h"
33 #include "qpyopengl_misc.h"
34 
35 
36 // Forward declarations.
37 static qpyopengl_dataCache *get_cache(PyObject *bindings);
38 static void *convert_values(Array *cache, PyObject *values,
39         GLenum gl_type, sipErrorState *estate);
40 static void convert_byte(PyObject *itm, void *array, Py_ssize_t i);
41 static void convert_ubyte(PyObject *itm, void *array, Py_ssize_t i);
42 static void convert_short(PyObject *itm, void *array, Py_ssize_t i);
43 static void convert_ushort(PyObject *itm, void *array, Py_ssize_t i);
44 static void convert_int(PyObject *itm, void *array, Py_ssize_t i);
45 static void convert_uint(PyObject *itm, void *array, Py_ssize_t i);
46 static void convert_float(PyObject *itm, void *array, Py_ssize_t i);
47 #if defined(SIP_FEATURE_PyQt_Desktop_OpenGL)
48 static void convert_double(PyObject *itm, void *array, Py_ssize_t i);
49 #endif
50 
51 
52 // Get an array of OpenGL fundamental types from a sequence or an object that
53 // implements a compatible buffer.
qpyopengl_value_array(sipErrorState * estate,PyObject * values,GLenum gl_type,PyObject * bindings)54 const GLvoid *qpyopengl_value_array(sipErrorState *estate, PyObject *values,
55         GLenum gl_type, PyObject *bindings)
56 {
57     // Handle the trivial None case first.  0 has a special meaning to some
58     // OpenGL calls so we allow it for all.  If this becomes a problem then we
59     // can add a new variant of this function that handles None differently.
60     if (values == Py_None)
61         return 0;
62 
63     qpyopengl_dataCache *data_cache = get_cache(bindings);
64 
65     if (!data_cache)
66     {
67         *estate = sipErrorFail;
68         return 0;
69     }
70 
71     // Get an empty wrapper for the array.
72     if (data_cache->uncached)
73         data_cache->uncached->clear();
74     else
75         data_cache->uncached = new Array;
76 
77     return convert_values(data_cache->uncached, values, gl_type, estate);
78 }
79 
80 
81 // Get an array of OpenGL fundamental types from a sequence or an object that
82 // implements a compatible buffer.  Cache the array so that it persists until a
83 // similar call.
qpyopengl_value_array_cached(sipErrorState * estate,PyObject * values,GLenum gl_type,PyObject * bindings,const char * pkey,GLuint skey)84 const GLvoid *qpyopengl_value_array_cached(sipErrorState *estate,
85         PyObject *values, GLenum gl_type, PyObject *bindings, const char *pkey,
86         GLuint skey)
87 {
88     // Handle the trivial byte offset case first.
89     PyErr_Clear();
90 
91     void *data = PyLong_AsVoidPtr(values);
92 
93     if (!PyErr_Occurred())
94         return data;
95 
96     PyErr_Clear();
97 
98     qpyopengl_dataCache *data_cache = get_cache(bindings);
99 
100     if (!data_cache)
101     {
102         *estate = sipErrorFail;
103         return 0;
104     }
105 
106     if (!data_cache->pcache)
107         data_cache->pcache = new PrimaryCache;
108 
109     // Get an empty wrapper for the array.
110     PrimaryCacheEntry *pce = (*data_cache->pcache)[pkey];
111 
112     if (!pce)
113     {
114         pce = new PrimaryCacheEntry;
115         data_cache->pcache->insert(pkey, pce);
116     }
117 
118     Array *array;
119 
120     if (skey == 0)
121     {
122         array = &pce->skey_0;
123     }
124     else
125     {
126         if (!pce->skey_n)
127             pce->skey_n = new SecondaryCache;
128 
129         array = (*pce->skey_n)[skey];
130 
131         if (!array)
132         {
133             array = new Array;
134             pce->skey_n->insert(skey, array);
135         }
136     }
137 
138     array->clear();
139 
140     return convert_values(array, values, gl_type, estate);
141 }
142 
143 
144 // Get the cache for a set of bindings.
get_cache(PyObject * bindings)145 static qpyopengl_dataCache *get_cache(PyObject *bindings)
146 {
147     // Create the cache if it doesn't already exist.
148     qpyopengl_dataCache *data_cache = (qpyopengl_dataCache *)sipGetUserObject((sipSimpleWrapper *)bindings);
149 
150     if (!data_cache)
151     {
152         data_cache = qpyopengl_dataCache_New();
153 
154         if (data_cache)
155             sipSetUserObject((sipSimpleWrapper *)bindings,
156                     (PyObject *)data_cache);
157     }
158 
159     return data_cache;
160 }
161 
162 
163 // Convert the Python values to a raw array.
convert_values(Array * array,PyObject * values,GLenum gl_type,sipErrorState * estate)164 static void *convert_values(Array *array, PyObject *values, GLenum gl_type,
165         sipErrorState *estate)
166 {
167 #if PY_VERSION_HEX >= 0x02060300
168     int rc = sipGetBufferInfo(values, &array->buffer);
169 
170     if (rc < 0)
171     {
172         *estate = sipErrorFail;
173         return 0;
174     }
175 
176     if (rc > 0)
177     {
178         // Check the buffer is compatible with what we need.
179         GLenum array_type;
180 
181         switch (*array->buffer.bi_format)
182         {
183         case 'b':
184             array_type = GL_BYTE;
185             break;
186 
187         case 'B':
188             array_type = GL_UNSIGNED_BYTE;
189             break;
190 
191         case 'h':
192             array_type = GL_SHORT;
193             break;
194 
195         case 'H':
196             array_type = GL_UNSIGNED_SHORT;
197             break;
198 
199         case 'i':
200             array_type = GL_INT;
201             break;
202 
203         case 'I':
204             array_type = GL_UNSIGNED_INT;
205             break;
206 
207         case 'f':
208             array_type = GL_FLOAT;
209             break;
210 
211 #if defined(SIP_FEATURE_PyQt_Desktop_OpenGL)
212         case 'd':
213             array_type = GL_DOUBLE;
214             break;
215 #endif
216 
217         default:
218             PyErr_Format(PyExc_TypeError, "unsupported buffer type '%s'",
219                     array->buffer.bi_format);
220             *estate = sipErrorFail;
221             return 0;
222         }
223 
224         if (array_type != gl_type)
225         {
226             PyErr_SetString(PyExc_TypeError,
227                     "the buffer type is not the same as the array type");
228             *estate = sipErrorFail;
229             return 0;
230         }
231 
232         return array->buffer.bi_buf;
233     }
234 #else
235     PyBufferProcs *bf = Py_TYPE(values)->tp_as_buffer;
236 
237     if (bf && bf->bf_getreadbuffer && bf->bf_getsegcount)
238     {
239         if (bf->bf_getsegcount(values, 0) != 1)
240         {
241             PyErr_SetString(PyExc_TypeError,
242                     "single-segment buffer object expected");
243             *estate = sipErrorFail;
244             return 0;
245         }
246 
247         if (bf->bf_getreadbuffer(values, 0, reinterpret_cast<void **>(&array)) < 0)
248         {
249             *estate = sipErrorFail;
250             return 0;
251         }
252 
253         Py_INCREF(values);
254         array->buffer = values;
255 
256         return array;
257     }
258 #endif
259 
260     PyObject *seq = PySequence_Fast(values,
261             "array must be a sequence or a buffer");
262 
263     if (!seq)
264     {
265         *estate = sipErrorContinue;
266         return 0;
267     }
268 
269     Py_ssize_t nr_items = Sequence_Fast_Size(seq);
270 
271     if (nr_items < 1)
272     {
273         Py_DECREF(seq);
274 
275         PyErr_SetString(PyExc_TypeError,
276                 "array must have at least one element");
277         *estate = sipErrorFail;
278         return 0;
279     }
280 
281     void (*convertor)(PyObject *, void *, Py_ssize_t);
282     size_t element_size;
283 
284     switch (gl_type)
285     {
286     case GL_BYTE:
287         convertor = convert_byte;
288         element_size = sizeof (GLbyte);
289         break;
290 
291     case GL_UNSIGNED_BYTE:
292         convertor = convert_ubyte;
293         element_size = sizeof (GLubyte);
294         break;
295 
296     case GL_SHORT:
297         convertor = convert_short;
298         element_size = sizeof (GLshort);
299         break;
300 
301     case GL_UNSIGNED_SHORT:
302         convertor = convert_ushort;
303         element_size = sizeof (GLushort);
304         break;
305 
306     case GL_INT:
307         convertor = convert_int;
308         element_size = sizeof (GLint);
309         break;
310 
311     case GL_UNSIGNED_INT:
312         convertor = convert_uint;
313         element_size = sizeof (GLuint);
314         break;
315 
316     case GL_FLOAT:
317         convertor = convert_float;
318         element_size = sizeof (GLfloat);
319         break;
320 
321 #if defined(SIP_FEATURE_PyQt_Desktop_OpenGL)
322 #if GL_DOUBLE != GL_FLOAT
323     case GL_DOUBLE:
324         convertor = convert_double;
325         element_size = sizeof (GLdouble);
326         break;
327 #endif
328 #endif
329 
330     default:
331         Py_DECREF(seq);
332 
333         PyErr_SetString(PyExc_TypeError, "unsupported GL element type");
334         *estate = sipErrorFail;
335         return 0;
336     }
337 
338     void *data = sipMalloc(nr_items * element_size);
339 
340     if (!data)
341     {
342         Py_DECREF(seq);
343 
344         *estate = sipErrorFail;
345         return 0;
346     }
347 
348     for (Py_ssize_t i = 0; i < nr_items; ++i)
349     {
350         PyErr_Clear();
351 
352         convertor(Sequence_Fast_GetItem(seq, i), data, i);
353 
354         if (PyErr_Occurred())
355         {
356             sipFree(data);
357             Py_DECREF(seq);
358 
359             *estate = sipErrorFail;
360             return 0;
361         }
362     }
363 
364     Py_DECREF(seq);
365 
366     array->data = data;
367 
368     return data;
369 }
370 
371 
372 // Convert a Python object to a GLbyte.
convert_byte(PyObject * itm,void * array,Py_ssize_t i)373 static void convert_byte(PyObject *itm, void *array, Py_ssize_t i)
374 {
375     reinterpret_cast<GLbyte *>(array)[i] = sipLong_AsSignedChar(itm);
376 }
377 
378 
379 // Convert a Python object to a GLubyte.
convert_ubyte(PyObject * itm,void * array,Py_ssize_t i)380 static void convert_ubyte(PyObject *itm, void *array, Py_ssize_t i)
381 {
382     reinterpret_cast<GLubyte *>(array)[i] = sipLong_AsUnsignedChar(itm);
383 }
384 
385 
386 // Convert a Python object to a GLshort.
convert_short(PyObject * itm,void * array,Py_ssize_t i)387 static void convert_short(PyObject *itm, void *array, Py_ssize_t i)
388 {
389     reinterpret_cast<GLshort *>(array)[i] = sipLong_AsShort(itm);
390 }
391 
392 
393 // Convert a Python object to a GLushort.
convert_ushort(PyObject * itm,void * array,Py_ssize_t i)394 static void convert_ushort(PyObject *itm, void *array, Py_ssize_t i)
395 {
396     reinterpret_cast<GLushort *>(array)[i] = sipLong_AsUnsignedShort(itm);
397 }
398 
399 
400 // Convert a Python object to a GLint.
convert_int(PyObject * itm,void * array,Py_ssize_t i)401 static void convert_int(PyObject *itm, void *array, Py_ssize_t i)
402 {
403     reinterpret_cast<GLint *>(array)[i] = sipLong_AsInt(itm);
404 }
405 
406 
407 // Convert a Python object to a GLuint.
convert_uint(PyObject * itm,void * array,Py_ssize_t i)408 static void convert_uint(PyObject *itm, void *array, Py_ssize_t i)
409 {
410     reinterpret_cast<GLuint *>(array)[i] = sipLong_AsUnsignedInt(itm);
411 }
412 
413 
414 // Convert a Python object to a GLfloat.
convert_float(PyObject * itm,void * array,Py_ssize_t i)415 static void convert_float(PyObject *itm, void *array, Py_ssize_t i)
416 {
417     reinterpret_cast<GLfloat *>(array)[i] = PyFloat_AsDouble(itm);
418 }
419 
420 
421 #if defined(SIP_FEATURE_PyQt_Desktop_OpenGL)
422 // Convert a Python object to a GLdouble.
convert_double(PyObject * itm,void * array,Py_ssize_t i)423 static void convert_double(PyObject *itm, void *array, Py_ssize_t i)
424 {
425     reinterpret_cast<GLdouble *>(array)[i] = PyFloat_AsDouble(itm);
426 }
427 #endif
428 
429 #endif
430 
431 
432 #endif
433