diff --git a/numpy/core/src/multiarray/ctors.c b/numpy/core/src/multiarray/ctors.c index 0017de0ad6e7..cec9318157f7 100644 --- a/numpy/core/src/multiarray/ctors.c +++ b/numpy/core/src/multiarray/ctors.c @@ -1259,6 +1259,57 @@ PyArray_New(PyTypeObject *subtype, int nd, npy_intp *dims, int type_num, return new; } +int +_copy_from_pil_style(char* dest, int ndim, char *buf, npy_intp *shape, + npy_intp *strides, npy_intp *suboffsets, npy_intp itemsize) +{ + npy_intp indices[NPY_MAXDIMS]; + void **pointers[NPY_MAXDIMS]; + char *pointer = (char*)buf; + npy_intp blocksize; + int ax; + /* Since suboffsets cannot be all negative, last_axis will be inevitably + * reset in the initialization loop below. However, the compiler cannot + * know this, so we preset last_axis to an arbitrary value. */ + int last_axis = 0; + + /* Initialize indices and pointers. */ + for (ax = 0; ax < ndim; ++ax) { + indices[ax] = 0; + pointers[ax] = (void **)pointer; + if (suboffsets[ax] >= 0) { + last_axis = ax; + pointer = *((char**)pointer) + suboffsets[ax]; + } + } + /* compute blocksize */ + blocksize = itemsize; + for (ax = last_axis + 1; ax < ndim; ++ax) + blocksize *= shape[ax]; + /* main loop */ + for (;;) { + memcpy(dest, pointer, blocksize); + dest += blocksize; + ax = last_axis; + /* Update indices and pointers for the next block. */ + while (indices[ax] == shape[ax] - 1) { + if (ax == 0) + return 0; + indices[ax--] = 0; + } + ++indices[ax]; + /* update pointers */ + pointer = (char *)pointers[ax] + strides[ax]; + for (; ax <= last_axis; ++ax) { + pointers[ax] = (void **)pointer; + if (suboffsets[ax] >=0 ) { + pointer = *((char**)pointer) + suboffsets[ax]; + } + } + } + return -1; +} + NPY_NO_EXPORT int _array_from_buffer_3118(PyObject *obj, PyObject **out) @@ -1344,23 +1395,43 @@ _array_from_buffer_3118(PyObject *obj, PyObject **out) } } - flags = NPY_ARRAY_BEHAVED & (view->readonly ? ~NPY_ARRAY_WRITEABLE : ~0); - r = PyArray_NewFromDescr(&PyArray_Type, descr, - nd, shape, strides, view->buf, - flags, NULL); - if (r == NULL || - PyArray_SetBaseObject((PyArrayObject *)r, memoryview) < 0) { - Py_XDECREF(r); - Py_DECREF(memoryview); - return -1; + if (view->suboffsets == NULL) { + flags = NPY_ARRAY_BEHAVED & (view->readonly ? ~NPY_ARRAY_WRITEABLE : ~0); + r = PyArray_NewFromDescr(&PyArray_Type, descr, + nd, shape, strides, view->buf, + flags, NULL); + if (r == NULL || + PyArray_SetBaseObject((PyArrayObject *)r, memoryview) < 0) { + Py_XDECREF(r); + Py_DECREF(memoryview); + return -1; + } + PyArray_UpdateFlags((PyArrayObject *)r, NPY_ARRAY_UPDATE_ALL); } - PyArray_UpdateFlags((PyArrayObject *)r, NPY_ARRAY_UPDATE_ALL); + else { + /* Recompute strides */ + npy_intp blocksize = view->itemsize; + for (k = nd - 1; k >= 0; --k) { + if (view->suboffsets[k] >= 0) { + strides[k] = blocksize; + } + blocksize *= shape[k]; + } + r = PyArray_NewFromDescr(&PyArray_Type, descr, + nd, shape, strides, NULL, 0, NULL); + if (r == NULL) + goto fail2; + _copy_from_pil_style((char *)PyArray_DATA((PyArrayObject *)r), nd, + (char *)view->buf, shape, view->strides, + view->suboffsets, view->itemsize); + } *out = r; return 0; fail: Py_XDECREF(descr); +fail2: Py_DECREF(memoryview); return -1; diff --git a/numpy/core/tests/test_multiarray.py b/numpy/core/tests/test_multiarray.py index 262fbc0c05cd..470f7ce55b42 100644 --- a/numpy/core/tests/test_multiarray.py +++ b/numpy/core/tests/test_multiarray.py @@ -5921,6 +5921,20 @@ def test_relaxed_strides(self): shape, strides = get_buffer_info(arr, ['C_CONTIGUOUS']) assert_(strides[-1] == 8) + def test_pil_style(self): + try: + import _testbuffer + except ImportError: + raise SkipTest("_testbuffer is not available") + + for shape in [(2, 3), (2, 3, 4)]: + data = list(range(np.prod(shape))) + buffer = _testbuffer.ndarray(data, shape, format='i', + flags=_testbuffer.ND_PIL) + a = np.array(buffer) + b = np.array(data, dtype='i').reshape(shape) + yield assert_equal, a, b + class TestArrayAttributeDeletion(object):