bgl module: Interpret a buffer as a bgl.Buffer
authorGermano <germano.costa@ig.com.br>
Wed, 27 Sep 2017 14:20:00 +0000 (11:20 -0300)
committerGermano <germano.costa@ig.com.br>
Wed, 27 Sep 2017 14:20:00 +0000 (11:20 -0300)
Differential Revision: https://developer.blender.org/D2857

source/blender/python/generic/bgl.c

index 3ea10228ad43c6d68f6f208e9f40828f1569c574..79d01d25e1745ae37f4f097c86c176e0b3caf54d 100644 (file)
@@ -472,6 +472,34 @@ int BGL_typeSize(int type)
        return -1;
 }
 
+static int gl_buffer_type_from_py_format_char(char format)
+{
+       switch (format) {
+               case 'b':
+                       return GL_BYTE;
+               case 'h':
+               case 'i':
+                       return GL_SHORT;
+               case 'l':
+                       return GL_INT;
+               case 'f':
+                       return GL_FLOAT;
+               case 'd':
+                       return GL_DOUBLE;
+       }
+       return -1; /* UNKNOWN */
+}
+
+static bool compare_dimensions(int ndim, int *dim1, Py_ssize_t *dim2)
+{
+       for (int i = 0; i < ndim; i++) {
+               if (dim1[i] != dim2[i]) {
+                       return false;
+               }
+       }
+       return true;
+}
+
 /** \} */
 
 
@@ -630,6 +658,22 @@ PyTypeObject BGL_bufferType = {
        NULL                        /*tp_del*/
 };
 
+
+static Buffer *BGL_MakeBuffer_FromData(PyObject *parent, int type, int ndimensions, int *dimensions, void *buf)
+{
+       Buffer *buffer = (Buffer *)PyObject_NEW(Buffer, &BGL_bufferType);
+
+       Py_XINCREF(parent);
+       buffer->parent = parent;
+       buffer->ndimensions = ndimensions;
+       buffer->dimensions = MEM_mallocN(ndimensions * sizeof(int), "Buffer dimensions");
+       memcpy(buffer->dimensions, dimensions, ndimensions * sizeof(int));
+       buffer->type = type;
+       buffer->buf.asvoid = buf;
+
+       return buffer;
+}
+
 /**
  * Create a buffer object
  *
@@ -641,30 +685,21 @@ Buffer *BGL_MakeBuffer(int type, int ndimensions, int *dimensions, void *initbuf
 {
        Buffer *buffer;
        void *buf = NULL;
-       int i, size, length;
+       int i, size = BGL_typeSize(type);
 
-       length = 1;
        for (i = 0; i < ndimensions; i++) {
-               length *= dimensions[i];
+               size *= dimensions[i];
        }
 
-       size = BGL_typeSize(type);
+       buf = MEM_mallocN(size, "Buffer buffer");
 
-       buf = MEM_mallocN(length * size, "Buffer buffer");
-
-       buffer = (Buffer *)PyObject_NEW(Buffer, &BGL_bufferType);
-       buffer->parent = NULL;
-       buffer->ndimensions = ndimensions;
-       buffer->dimensions = MEM_mallocN(ndimensions * sizeof(int), "Buffer dimensions");
-       memcpy(buffer->dimensions, dimensions, ndimensions * sizeof(int));
-       buffer->type = type;
-       buffer->buf.asvoid = buf;
+       buffer = BGL_MakeBuffer_FromData(NULL, type, ndimensions, dimensions, buf);
 
        if (initbuffer) {
-               memcpy(buffer->buf.asvoid, initbuffer, length * size);
+               memcpy(buffer->buf.asvoid, initbuffer, size);
        }
        else {
-               memset(buffer->buf.asvoid, 0, length * size);
+               memset(buffer->buf.asvoid, 0, size);
        }
        return buffer;
 }
@@ -674,7 +709,7 @@ Buffer *BGL_MakeBuffer(int type, int ndimensions, int *dimensions, void *initbuf
 static PyObject *Buffer_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject *kwds)
 {
        PyObject *length_ob = NULL, *init = NULL;
-       Buffer *buffer;
+       Buffer *buffer = NULL;
        int dimensions[MAX_DIMENSIONS];
 
        int type;
@@ -739,9 +774,32 @@ static PyObject *Buffer_new(PyTypeObject *UNUSED(type), PyObject *args, PyObject
                return NULL;
        }
 
-       buffer = BGL_MakeBuffer(type, ndimensions, dimensions, NULL);
-       if (init && ndimensions) {
-               if (Buffer_ass_slice(buffer, 0, dimensions[0], init)) {
+       if (init && PyObject_CheckBuffer(init)) {
+               Py_buffer pybuffer;
+
+               if (PyObject_GetBuffer(init, &pybuffer, PyBUF_ND | PyBUF_FORMAT) == -1) {
+                       /* PyObject_GetBuffer raise a PyExc_BufferError */
+                       return NULL;
+               }
+
+               if (type != gl_buffer_type_from_py_format_char(*pybuffer.format)) {
+                       PyErr_Format(PyExc_TypeError,
+                                    "`GL_TYPE` and `format` of object with buffer interface do not match");
+               }
+               else if (ndimensions != pybuffer.ndim ||
+                       !compare_dimensions(ndimensions, dimensions, pybuffer.shape))
+               {
+                       PyErr_Format(PyExc_TypeError, "array size does not match");
+               }
+               else {
+                       buffer = BGL_MakeBuffer_FromData(init, type, pybuffer.ndim, dimensions, pybuffer.buf);
+               }
+
+               PyBuffer_Release(&pybuffer);
+       }
+       else {
+               buffer = BGL_MakeBuffer(type, ndimensions, dimensions, NULL);
+               if (init && Buffer_ass_slice(buffer, 0, dimensions[0], init)) {
                        Py_DECREF(buffer);
                        return NULL;
                }
@@ -774,27 +832,17 @@ static PyObject *Buffer_item(Buffer *self, int i)
                }
        }
        else {
-               Buffer *newbuf;
-               int j, length, size;
+               int j, offset = i * BGL_typeSize(self->type);
 
-               length = 1;
                for (j = 1; j < self->ndimensions; j++) {
-                       length *= self->dimensions[j];
+                       offset *= self->dimensions[j];
                }
-               size = BGL_typeSize(self->type);
-
-               newbuf = (Buffer *)PyObject_NEW(Buffer, &BGL_bufferType);
-
-               Py_INCREF(self);
-               newbuf->parent = (PyObject *)self;
-
-               newbuf->ndimensions = self->ndimensions - 1;
-               newbuf->type = self->type;
-               newbuf->buf.asvoid = self->buf.asbyte + i * length * size;
-               newbuf->dimensions = MEM_mallocN(newbuf->ndimensions * sizeof(int), "Buffer dimensions");
-               memcpy(newbuf->dimensions, self->dimensions + 1, newbuf->ndimensions * sizeof(int));
 
-               return (PyObject *)newbuf;
+               return BGL_MakeBuffer_FromData(
+                       self, self->type,
+                       self->ndimensions - 1,
+                       self->dimensions + 1,
+                       self->buf.asbyte + offset);
        }
 
        return NULL;