diff --git a/src/pymmcore/pymmcore_swig.i b/src/pymmcore/pymmcore_swig.i index 9276c08..bdd3c7a 100644 --- a/src/pymmcore/pymmcore_swig.i +++ b/src/pymmcore/pymmcore_swig.i @@ -55,33 +55,44 @@ import_array(); %typemap(out) void* { - npy_intp dims[2]; - dims[0] = (arg1)->getImageHeight(); - dims[1] = (arg1)->getImageWidth(); - npy_intp pixelCount = dims[0] * dims[1]; + // nullptr, return None + if (result == NULL) { + Py_INCREF(Py_None); + $result = Py_None; + return $result; + } + + int width, height, bytesPerPixel, numComponents; + (arg1)->getImageProperties(result, width, height, bytesPerPixel, numComponents); + int pixelCount = width * height; + npy_intp dims[2] = {height, width}; - if ((arg1)->getBytesPerPixel() == 1) + if (bytesPerPixel == 1) { PyObject * numpyArray = PyArray_SimpleNew(2, dims, NPY_UINT8); memcpy(PyArray_DATA((PyArrayObject *) numpyArray), result, pixelCount); + (arg1)->releaseReadAccess(result); $result = numpyArray; } - else if ((arg1)->getBytesPerPixel() == 2) + else if (bytesPerPixel == 2) { PyObject * numpyArray = PyArray_SimpleNew(2, dims, NPY_UINT16); memcpy(PyArray_DATA((PyArrayObject *) numpyArray), result, pixelCount * 2); + (arg1)->releaseReadAccess(result); $result = numpyArray; } - else if ((arg1)->getBytesPerPixel() == 4) + else if (bytesPerPixel == 4) { PyObject * numpyArray = PyArray_SimpleNew(2, dims, NPY_UINT32); memcpy(PyArray_DATA((PyArrayObject *) numpyArray), result, pixelCount * 4); + (arg1)->releaseReadAccess(result); $result = numpyArray; } - else if ((arg1)->getBytesPerPixel() == 8) + else if (bytesPerPixel == 8) { PyObject * numpyArray = PyArray_SimpleNew(2, dims, NPY_UINT64); memcpy(PyArray_DATA((PyArrayObject *) numpyArray), result, pixelCount * 8); + (arg1)->releaseReadAccess(result); $result = numpyArray; } else @@ -94,53 +105,94 @@ import_array(); } } +// This is conceptually similar to the void* typemap above, +// but requires slightly different calls because BufferDataPointer +// is different from the data-returning void* methods of the Core. +%typemap(out) BufferDataPointerVoidStar { + + npy_intp numBytes = (arg1)->getSizeBytes(); + // nullptr, return None + if (numBytes == 0) { + Py_INCREF(Py_None); + $result = Py_None; + return $result; + } -%typemap(out) unsigned int* -{ - //Here we assume we are getting RGBA (32 bits). - npy_intp dims[3]; - dims[0] = (arg1)->getImageHeight(); - dims[1] = (arg1)->getImageWidth(); - dims[2] = 3; // RGB - unsigned numChannels = (arg1)->getNumberOfComponents(); - unsigned char * pyBuf; - unsigned char * coreBuf = (unsigned char *) result; - - if ((arg1)->getBytesPerPixel() == 4 && numChannels == 1) - { - - // create new numpy array object - PyObject * numpyArray = PyArray_SimpleNew(3, dims, NPY_UINT8); - - // get a pointer to the data buffer - pyBuf = (unsigned char *) PyArray_DATA((PyArrayObject *) numpyArray); - - // copy R,G,B but leave out A in RGBA to return a WxHx3-dimensional array - - long pixelCount = dims[0] * dims[1]; - - for (long i=0; igetImageProperties(width, height, bytesPerPixel, numComponents); + + // If getImageProperties fails, its not image data. Assume 1 byte per pixel + // If more data types are supported in the future, could add other + // checks here to return other data types. + if (!propertiesOK) { + bytesPerPixel = 1; + numComponents = 1; + int pixelCount = numBytes; + npy_intp dims[1] = {pixelCount}; + + PyObject * numpyArray = PyArray_SimpleNew(1, dims, NPY_UINT8); + memcpy(PyArray_DATA((PyArrayObject *) numpyArray), result, pixelCount); $result = numpyArray; + return $result; + } + + int pixelCount = width * height; + npy_intp dims[2] = {height, width}; + if (bytesPerPixel == 1) + { + PyObject * numpyArray = PyArray_SimpleNew(2, dims, NPY_UINT8); + memcpy(PyArray_DATA((PyArrayObject *) numpyArray), result, pixelCount); + // No realease here because that is done explicitly for BufferDataPointer + $result = numpyArray; + } + else if (bytesPerPixel == 2) + { + PyObject * numpyArray = PyArray_SimpleNew(2, dims, NPY_UINT16); + memcpy(PyArray_DATA((PyArrayObject *) numpyArray), result, pixelCount * 2); + // No realease here because that is done explicitly for BufferDataPointer + $result = numpyArray; + } + else if (bytesPerPixel == 4) + { + PyObject * numpyArray = PyArray_SimpleNew(2, dims, NPY_UINT32); + memcpy(PyArray_DATA((PyArrayObject *) numpyArray), result, pixelCount * 4); + // No realease here because that is done explicitly for BufferDataPointer + $result = numpyArray; + } + else if (bytesPerPixel == 8) + { + PyObject * numpyArray = PyArray_SimpleNew(2, dims, NPY_UINT64); + memcpy(PyArray_DATA((PyArrayObject *) numpyArray), result, pixelCount * 8); + // No realease here because that is done explicitly for BufferDataPointer + $result = numpyArray; } else { // don't know how to map // TODO: thow exception? + // XXX Must do something, as returning NULL without setting error results + // in an opaque error. $result = 0; - } + } +} + + +// Unlike void* above, this alias to void* is mapped to long so it can be used as a pointer +// address instead of having the data it points to copied +%typemap(out) DataPtr { + // Convert the DataPtr to a Python integer + $result = PyLong_FromVoidPtr((void *)$1); +} + +%typemap(in) DataPtr { + // Convert the Python integer back to a DataPtr + $1 = (DataPtr)PyLong_AsVoidPtr($input); } + + + /* tell SWIG to treat char ** as a list of strings */ /* From https://stackoverflow.com/questions/3494598/passing-a-list-of-strings-to-from-python-ctypes-to-c-function-expecting-char */ /* XXX No need to freearg the vector, right? */ @@ -288,6 +340,7 @@ import_array(); #include "ImageMetadata.h" #include "MMEventCallback.h" #include "MMCore.h" +#include "NewDataBufferPointer.h" %} // Exception handling. Tranditionally, MMCore uses exception specifications @@ -395,9 +448,21 @@ namespace std { %apply int &OUTPUT { int &xSize }; %apply int &OUTPUT { int &ySize }; +%apply int &OUTPUT { int &width }; +%apply int &OUTPUT { int &height }; +%apply int &OUTPUT { int &byteDepth }; +%apply int &OUTPUT { int &nComponents }; + +// These are needed by the void* typemaps to copy pixels and then +// release them, but they shouldn't be needed by pymmcore +// because their functionality is handled by the BufferDataPointer class +%ignore CMMCore::getImageProperties(DataPtr, int&, int&, int&, int&); +%ignore CMMCore::releaseReadAccess(DataPtr); + %include "MMDeviceConstants.h" %include "Error.h" %include "Configuration.h" %include "MMCore.h" %include "ImageMetadata.h" %include "MMEventCallback.h" +%include "NewDataBufferPointer.h"