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