From f0b21ee969c00fd815ff1b05ad0d1b2cec229c1f Mon Sep 17 00:00:00 2001 From: rdb Date: Wed, 20 Dec 2017 01:23:00 +0100 Subject: [PATCH] Support old Python 2 buffer protocol in PTA and Texture ram_image This enables passing eg. str and array.array objects in Python 2 --- panda/src/express/pointerToArray_ext.I | 37 ++++++++++++++------------ panda/src/gobj/texture_ext.cxx | 23 ++++++++++++++++ 2 files changed, 43 insertions(+), 17 deletions(-) diff --git a/panda/src/express/pointerToArray_ext.I b/panda/src/express/pointerToArray_ext.I index e2356c7a4b..644b5f0645 100644 --- a/panda/src/express/pointerToArray_ext.I +++ b/panda/src/express/pointerToArray_ext.I @@ -203,34 +203,37 @@ set_data(PyObject *data) { } PyBuffer_Release(&view); - } else { - Dtool_Raise_TypeError("PointerToArray.set_data() requires a buffer object"); + return; } -#else - // In Python 2.5 we didn't have the new buffer protocol, only str. - if (PyString_CheckExact(data)) { - int size = PyString_Size(data); - if (size % sizeof(Element) != 0) { +#endif + + // In Python 2, there was also an older buffer protocol, supported by eg. + // str and array objects. +#if PY_MAJOR_VERSION < 3 + // The old, deprecated buffer interface, as used by eg. the array module. + const void *buffer; + Py_ssize_t buffer_len; + if (!PyUnicode_CheckExact(data) && + PyObject_AsReadBuffer(data, &buffer, &buffer_len) == 0) { + if (buffer_len % sizeof(Element) != 0) { PyErr_Format(PyExc_ValueError, - "str object is not a multiple of %zu bytes", + "byte buffer is not a multiple of %zu bytes", sizeof(Element)); return; } - int num_elements = size / sizeof(Element); - this->_this->insert(this->_this->begin(), num_elements, Element()); - - // Hope there aren't any constructors or destructors involved here. - if (size != 0) { - const char *ptr = PyString_AsString(data); - memcpy(this->_this->p(), ptr, size); + if (buffer_len > 0) { + this->_this->resize(buffer_len / sizeof(Element)); + memcpy(this->_this->p(), buffer, buffer_len); } else { this->_this->clear(); } - } else { - Dtool_Raise_TypeError("PointerToArray.set_data() requires a str"); + + return; } #endif + + Dtool_Raise_TypeError("PointerToArray.set_data() requires a buffer object"); } /** diff --git a/panda/src/gobj/texture_ext.cxx b/panda/src/gobj/texture_ext.cxx index 260070f058..e81d40c15f 100644 --- a/panda/src/gobj/texture_ext.cxx +++ b/panda/src/gobj/texture_ext.cxx @@ -84,6 +84,29 @@ set_ram_image(PyObject *image, Texture::CompressionMode compression, } #endif +#if PY_MAJOR_VERSION < 3 + // The old, deprecated buffer interface, as used by eg. the array module. + const void *buffer; + Py_ssize_t buffer_len; + if (!PyUnicode_CheckExact(image) && + PyObject_AsReadBuffer(image, &buffer, &buffer_len) == 0) { + if (compression == Texture::CM_off) { + int component_width = _this->get_component_width(); + if (buffer_len % component_width != 0) { + PyErr_Format(PyExc_ValueError, + "byte buffer is not a multiple of %d bytes", + component_width); + return; + } + } + + PTA_uchar data = PTA_uchar::empty_array(buffer_len, Texture::get_class_type()); + memcpy(data.p(), buffer, buffer_len); + _this->set_ram_image(MOVE(data), compression, page_size); + return; + } +#endif + Dtool_Raise_ArgTypeError(image, 0, "Texture.set_ram_image", "CPTA_uchar or buffer"); }