Browse Source

python: store memory owner references in the holder type instead.

Compared to having to subclass every type that can reference external
data, this has several advantages for 3rd party binding code:

 * it doesn't need to worry about the additional type when binding
   function arguments (currently it had to provide lambdas that accept
   the PyFoo subtype instead of just Foo)
 * and it can now easily bind those types also for function
   return values and properties -- the return type doesn't need to be
   subclassed (which in case of move-only types is practically
   impossible) but instead just wrapped in a holder along with the
   memory owner object reference

The new holders also assert that memory owner is always specified unless
the data is empty.
pull/2/head
Vladimír Vondruš 7 years ago
parent
commit
76d7ee0dc7
  1. 30
      src/Corrade/Containers/CMakeLists.txt
  2. 56
      src/Corrade/Containers/Python.h
  3. 39
      src/Corrade/Python.h
  4. 22
      src/Magnum/Python.h
  5. 8
      src/python/corrade/CMakeLists.txt
  6. 67
      src/python/corrade/PyArrayView.h
  7. 208
      src/python/corrade/containers.cpp
  8. 8
      src/python/magnum/gl.cpp
  9. 50
      src/python/magnum/magnum.cpp

30
src/Corrade/Containers/CMakeLists.txt

@ -0,0 +1,30 @@
#
# This file is part of Magnum.
#
# Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019
# Vladimír Vondruš <mosra@centrum.cz>
#
# Permission is hereby granted, free of charge, to any person obtaining a
# copy of this software and associated documentation files (the "Software"),
# to deal in the Software without restriction, including without limitation
# the rights to use, copy, modify, merge, publish, distribute, sublicense,
# and/or sell copies of the Software, and to permit persons to whom the
# Software is furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included
# in all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
# THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
# DEALINGS IN THE SOFTWARE.
#
if(WITH_PYTHON)
add_custom_target(CorradeContainersPython SOURCES Python.h)
set_target_properties(CorradeContainersPython PROPERTIES FOLDER "Corrade/Python")
install(FILES Python.h DESTINATION ${CORRADE_INCLUDE_INSTALL_DIR}/Containers)
endif()

56
src/Corrade/Containers/Python.h

@ -0,0 +1,56 @@
#ifndef Corrade_Containers_Python_h
#define Corrade_Containers_Python_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019
Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <memory> /* :( */
#include <pybind11/pybind11.h>
namespace Corrade { namespace Containers {
/* Stores additional stuff needed for proper refcounting of array views. Better
than subclassing ArrayView because then we would need to wrap it every time
it's exposed to Python, making 3rd party bindings unnecessarily complex. */
template<class T> struct PyArrayViewHolder: std::unique_ptr<T> {
explicit PyArrayViewHolder(T* object): PyArrayViewHolder{object, pybind11::none{}} {
/* Array view without an owner can only be empty */
CORRADE_INTERNAL_ASSERT(!object->data());
}
explicit PyArrayViewHolder(T* object, pybind11::object owner): std::unique_ptr<T>{object}, owner{std::move(owner)} {}
pybind11::object owner;
};
template<class T> PyArrayViewHolder<T> pyArrayViewHolder(const T& view, pybind11::object owner) {
return PyArrayViewHolder<T>{new T{view}, owner};
}
}}
PYBIND11_DECLARE_HOLDER_TYPE(T, Corrade::Containers::PyArrayViewHolder<T>)
#endif

39
src/Corrade/Python.h

@ -26,7 +26,7 @@
*/
#include <pybind11/pybind11.h>
#include <Corrade/Utility/Assert.h>
#include <Corrade/Containers/ArrayView.h>
namespace Corrade {
@ -51,6 +51,43 @@ template<class T> inline T& pyInstanceFromHandle(pybind11::handle handle) {
return caster;
}
/* py::cast() doesn't work on holder types because it takes const T&. Fuck
that. The casting "just works" for function return types, so instead reuse
the stuff that's done inside py::class_::def(). */
template<template<class> class Holder, class T> pybind11::object pyCastButNotShitty(Holder<T>&& holder) {
static_assert(std::is_base_of<std::unique_ptr<T>, Holder<T>>::value,
"holder should be a subclass of std::unique_ptr");
/* Extracted out of cpp_function::initialize(), the cast_out alias. Not
*exactly* sure about the return value policy or parent. Stealing the
reference because cpp_function::dispatcher() seems to do that too (and
using reinterpret_borrow makes tests fail with too high refcount) */
return pybind11::reinterpret_steal<pybind11::object>(pybind11::detail::make_caster<Holder<T>>::cast(std::move(holder), pybind11::return_value_policy::move, {}));
}
/* Extracted the simplest case from py::type_caster_generic::load_impl() */
template<class T> T& pyObjectHolderFor(pybind11::handle obj, pybind11::detail::type_info* typeinfo) {
/* So we don't need to bother with
copyable_holder_caster::check_holder_compat() */
static_assert(!std::is_copy_constructible<T>::value,
"holder should be a move-only type");
/* Assume the type is not subclassed on Python side */
CORRADE_INTERNAL_ASSERT(Py_TYPE(obj.ptr()) == typeinfo->type);
const pybind11::detail::value_and_holder vh = reinterpret_cast<pybind11::detail::instance*>(obj.ptr())->get_value_and_holder();
/* And its already created and everything (i.e., we're not calling it in
its own constructor) */
CORRADE_INTERNAL_ASSERT(vh.holder_constructed());
CORRADE_INTERNAL_ASSERT(vh.instance_registered());
return vh.holder<T>();
}
template<template<class> class T, class U> T<U>& pyObjectHolderFor(U& obj) {
/* Not using pyHandleFromInstance in order to avoid calling get_type_info
more than once. I bet it involves some std::unordered_map access and
that's like the slowest stuff ever. */
pybind11::detail::type_info* typeinfo = pybind11::detail::get_type_info(typeid(U));
return pyObjectHolderFor<T<U>>(pybind11::detail::get_object_handle(&obj, typeinfo), typeinfo);
}
}
#endif

22
src/Magnum/Python.h

@ -25,19 +25,31 @@
DEALINGS IN THE SOFTWARE.
*/
#include <memory> /* :( */
#include <pybind11/pybind11.h>
#include <Magnum/ImageView.h>
namespace Magnum {
/* Wrapper for ImageView holding a reference to the memory owner */
template<UnsignedInt dimensions, class T> struct PyImageView: ImageView<dimensions, T> {
/*implicit*/ PyImageView() noexcept: owner{pybind11::none{}} {}
explicit PyImageView(const ImageView<dimensions, T>& view, pybind11::object owner) noexcept: ImageView<dimensions, T>{view}, owner{std::move(owner)} {}
/* Stores additional stuff needed for proper refcounting of image views. Better
than subclassing ImageView because then we would need to wrap it every time
it's exposed to Python, making 3rd party bindings unnecessarily complex */
template<class T> struct PyImageViewHolder: std::unique_ptr<T> {
explicit PyImageViewHolder(T* object): PyImageViewHolder{object, pybind11::none{}} {
/* Image view without an owner can only be empty */
CORRADE_INTERNAL_ASSERT(!object->data());
}
explicit PyImageViewHolder(T* object, pybind11::object owner): std::unique_ptr<T>{object}, owner{std::move(owner)} {}
pybind11::object owner;
};
template<class T> PyImageViewHolder<T> pyImageViewHolder(const T& view, pybind11::object owner) {
return PyImageViewHolder<T>{new T{view}, owner};
}
}
PYBIND11_DECLARE_HOLDER_TYPE(T, Magnum::PyImageViewHolder<T>)
#endif

8
src/python/corrade/CMakeLists.txt

@ -30,7 +30,9 @@ set(corrade_containers_SRCS
# modules
if(NOT CORRADE_BUILD_STATIC)
pybind11_add_module(corrade_containers SYSTEM ${corrade_containers_SRCS})
target_include_directories(corrade_containers PRIVATE ${PROJECT_SOURCE_DIR}/src/python)
target_include_directories(corrade_containers PRIVATE
${PROJECT_SOURCE_DIR}/src
${PROJECT_SOURCE_DIR}/src/python)
target_link_libraries(corrade_containers PRIVATE
Corrade::Containers
Corrade::Utility)
@ -48,7 +50,9 @@ else()
${corrade_containers_SRCS})
pybind11_add_module(corrade SYSTEM ${corrade_SRCS})
target_include_directories(corrade PRIVATE ${PROJECT_SOURCE_DIR}/src/python)
target_include_directories(corrade PRIVATE
${PROJECT_SOURCE_DIR}/src
${PROJECT_SOURCE_DIR}/src/python)
target_link_libraries(corrade PRIVATE Corrade::Utility ${corrade_LIBS})
set_target_properties(corrade PROPERTIES
FOLDER "python"

67
src/python/corrade/PyArrayView.h

@ -1,67 +0,0 @@
#ifndef corrade_PyArrayView_h
#define corrade_PyArrayView_h
/*
This file is part of Magnum.
Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019
Vladimír Vondruš <mosra@centrum.cz>
Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included
in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.
*/
#include <pybind11/pybind11.h>
#include <Corrade/Containers/ArrayView.h>
#include <Corrade/Containers/StridedArrayView.h>
#include "corrade/bootstrap.h"
namespace corrade {
/* Wrapper for Containers::ArrayView holding a reference to the memory owner */
template<class T> struct PyArrayView: Containers::ArrayView<T> {
/*implicit*/PyArrayView() noexcept: obj{py::none{}} {}
explicit PyArrayView(Containers::ArrayView<T> view, py::object obj) noexcept: Containers::ArrayView<T>{view}, obj{std::move(obj)} {}
/* Turning this into a reference so buffer protocol can point to it instead
of needing to allocate */
std::size_t& sizeRef() { return Containers::ArrayView<T>::_size; }
py::object obj;
};
/* Wrapper for Containers::StridedArrayView holding a reference to the memory owner */
template<unsigned dimensions, class T> struct PyStridedArrayView: Containers::StridedArrayView<dimensions, T> {
/*implicit*/ PyStridedArrayView() noexcept: obj{py::none{}} {}
explicit PyStridedArrayView(Containers::StridedArrayView<dimensions, T> view, py::object obj) noexcept: Containers::StridedArrayView<dimensions, T>{view}, obj{std::move(obj)} {}
/* Turning this into a reference so buffer protocol can point to it instead
of needing to allocate */
Containers::StridedDimensions<dimensions, std::size_t>& sizeRef() {
return Containers::StridedArrayView<dimensions, T>::_size;
}
Containers::StridedDimensions<dimensions, std::ptrdiff_t>& strideRef() {
return Containers::StridedArrayView<dimensions, T>::_stride;
}
py::object obj;
};
}
#endif

208
src/python/corrade/containers.cpp

@ -29,8 +29,9 @@
#include <Corrade/Containers/ScopeGuard.h>
#include <Corrade/Utility/FormatStl.h>
#include "Corrade/Containers/Python.h"
#include "corrade/bootstrap.h"
#include "corrade/PyArrayView.h"
#include "corrade/PyBuffer.h"
namespace corrade {
@ -88,7 +89,7 @@ template<class T> bool arrayViewBufferProtocol(T& self, Py_buffer& buffer, int f
if(flags != PyBUF_SIMPLE) {
/* The view is immutable (can't change its size after it has been
constructed), so referencing the size directly is okay */
buffer.shape = reinterpret_cast<Py_ssize_t*>(&self.sizeRef());
buffer.shape = reinterpret_cast<Py_ssize_t*>(&Containers::Implementation::sizeRef(self));
if((flags & PyBUF_STRIDES) == PyBUF_STRIDES)
buffer.strides = &buffer.itemsize;
}
@ -96,11 +97,11 @@ template<class T> bool arrayViewBufferProtocol(T& self, Py_buffer& buffer, int f
return true;
}
template<class T> void arrayView(py::class_<PyArrayView<T>>& c) {
template<class T> void arrayView(py::class_<Containers::ArrayView<T>, Containers::PyArrayViewHolder<Containers::ArrayView<T>>>& c) {
/* Implicitly convertible from a buffer */
py::implicitly_convertible<py::buffer, PyArrayView<T>>();
py::implicitly_convertible<py::buffer, Containers::ArrayView<T>>();
/* This is needed for implicit conversion from np.array */
py::implicitly_convertible<py::array, PyArrayView<T>>();
py::implicitly_convertible<py::array, Containers::ArrayView<T>>();
c
/* Constructor */
@ -125,45 +126,46 @@ template<class T> void arrayView(py::class_<PyArrayView<T>>& c) {
the buffer because we no longer care about the buffer
descriptor -- that could allow the GC to haul away a bit more
garbage */
return PyArrayView<T>{{static_cast<T*>(buffer.buf), std::size_t(buffer.len)}, py::reinterpret_borrow<py::object>(buffer.obj)};
return Containers::pyArrayViewHolder(Containers::ArrayView<T>{static_cast<T*>(buffer.buf), std::size_t(buffer.len)}, py::reinterpret_borrow<py::object>(buffer.obj));
}), "Construct from a buffer")
/* Length and memory owning object */
.def("__len__", &PyArrayView<T>::size, "View size")
.def_readonly("obj", &PyArrayView<T>::obj, "Memory owner object")
.def("__len__", &Containers::ArrayView<T>::size, "View size")
.def_property_readonly("obj", [](const Containers::ArrayView<T>& self) {
return pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner;
}, "Memory owner object")
/* Conversion to bytes */
.def("__bytes__", [](const PyArrayView<T>& self) {
.def("__bytes__", [](const Containers::ArrayView<T>& self) {
return py::bytes(self.data(), self.size());
}, "Convert to bytes")
/* Single item retrieval. Need to throw IndexError in order to allow
iteration: https://docs.python.org/3/reference/datamodel.html#object.__getitem__ */
.def("__getitem__", [](const PyArrayView<T>& self, std::size_t i) {
.def("__getitem__", [](const Containers::ArrayView<T>& self, std::size_t i) {
if(i >= self.size()) throw pybind11::index_error{};
return self[i];
}, "Value at given position")
/* Slicing */
.def("__getitem__", [](const PyArrayView<T>& self, py::slice slice) -> py::object {
.def("__getitem__", [](const Containers::ArrayView<T>& self, py::slice slice) -> py::object {
const Slice calculated = calculateSlice(slice, self.size());
/* Non-trivial stride, return a different type */
if(calculated.step != 1) {
return py::cast(PyStridedArrayView<1, T>(
Containers::stridedArrayView(self).slice(calculated.start, calculated.stop).every(calculated.step), self.obj));
return pyCastButNotShitty(Containers::pyArrayViewHolder(Containers::stridedArrayView(self).slice(calculated.start, calculated.stop).every(calculated.step), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner));
}
/* Usual business */
return py::cast(PyArrayView<T>{self.slice(calculated.start, calculated.stop), self.obj});
return pyCastButNotShitty(Containers::pyArrayViewHolder(self.slice(calculated.start, calculated.stop), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner));
}, "Slice the view");
enableBetterBufferProtocol<PyArrayView<T>, arrayViewBufferProtocol>(c);
enableBetterBufferProtocol<Containers::ArrayView<T>, arrayViewBufferProtocol>(c);
}
template<class T> void mutableArrayView(py::class_<PyArrayView<T>>& c) {
template<class T> void mutableArrayView(py::class_<Containers::ArrayView<T>, Containers::PyArrayViewHolder<Containers::ArrayView<T>>>& c) {
c
.def("__setitem__", [](const PyArrayView<T>& self, std::size_t i, const T& value) {
.def("__setitem__", [](const Containers::ArrayView<T>& self, std::size_t i, const T& value) {
if(i >= self.size()) throw pybind11::index_error{};
self[i] = value;
}, "Set a value at given position");
@ -281,15 +283,15 @@ template<class T> bool stridedArrayViewBufferProtocol(T& self, Py_buffer& buffer
buffer.itemsize = sizeof(typename T::Type);
buffer.len = sizeof(typename T::Type);
for(std::size_t i = 0; i != T::Dimensions; ++i)
buffer.len *= self.sizeRef()[i];
buffer.len *= Containers::Implementation::sizeRef(self)[i];
buffer.buf = const_cast<typename std::decay<typename T::ErasedType>::type*>(self.data());
buffer.readonly = std::is_const<typename T::Type>::value;
if((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
buffer.format = const_cast<char*>(FormatStrings[formatIndex<typename std::decay<typename T::Type>::type>()]);
/* The view is immutable (can't change its size after it has been
constructed), so referencing the size/stride directly is okay */
buffer.shape = const_cast<Py_ssize_t*>(reinterpret_cast<const Py_ssize_t*>(self.sizeRef().begin()));
buffer.strides = const_cast<Py_ssize_t*>(reinterpret_cast<const Py_ssize_t*>(self.strideRef().begin()));
buffer.shape = const_cast<Py_ssize_t*>(reinterpret_cast<const Py_ssize_t*>(Containers::Implementation::sizeRef(self).begin()));
buffer.strides = const_cast<Py_ssize_t*>(reinterpret_cast<const Py_ssize_t*>(Containers::Implementation::strideRef(self).begin()));
return true;
}
@ -298,11 +300,11 @@ inline std::size_t largerStride(std::size_t a, std::size_t b) {
return a < b ? b : a; /* max(), but named like this to avoid clashes */
}
template<unsigned dimensions, class T> void stridedArrayView(py::class_<PyStridedArrayView<dimensions, T>>& c) {
template<unsigned dimensions, class T> void stridedArrayView(py::class_<Containers::StridedArrayView<dimensions, T>, Containers::PyArrayViewHolder<Containers::StridedArrayView<dimensions, T>>>& c) {
/* Implicitly convertible from a buffer */
py::implicitly_convertible<py::buffer, PyStridedArrayView<dimensions, T>>();
py::implicitly_convertible<py::buffer, Containers::StridedArrayView<dimensions, T>>();
/* This is needed for implicit conversion from np.array */
py::implicitly_convertible<py::array, PyStridedArrayView<dimensions, T>>();
py::implicitly_convertible<py::array, Containers::StridedArrayView<dimensions, T>>();
c
/* Constructor */
@ -332,66 +334,68 @@ template<unsigned dimensions, class T> void stridedArrayView(py::class_<PyStride
the buffer because we no longer care about the buffer
descriptor -- that could allow the GC to haul away a bit more
garbage */
return PyStridedArrayView<dimensions, T>{{
return Containers::pyArrayViewHolder(Containers::StridedArrayView<dimensions, T>{
{static_cast<T*>(buffer.buf), size},
Containers::StaticArrayView<dimensions, const std::size_t>{reinterpret_cast<std::size_t*>(buffer.shape)},
Containers::StaticArrayView<dimensions, const std::ptrdiff_t>{reinterpret_cast<std::ptrdiff_t*>(buffer.strides)}},
py::reinterpret_borrow<py::object>(buffer.obj)};
py::reinterpret_borrow<py::object>(buffer.obj));
}), "Construct from a buffer")
/* Length, size/stride tuple, dimension count and memory owning object */
.def("__len__", [](const PyStridedArrayView<dimensions, T>& self) {
.def("__len__", [](const Containers::StridedArrayView<dimensions, T>& self) {
return Containers::StridedDimensions<dimensions, std::size_t>(self.size())[0];
}, "View size in the top-level dimension")
.def_property_readonly("size", [](const PyStridedArrayView<dimensions, T>& self) {
.def_property_readonly("size", [](const Containers::StridedArrayView<dimensions, T>& self) {
return size<dimensions>(self.size());
}, "View size in each dimension")
.def_property_readonly("stride", [](const PyStridedArrayView<dimensions, T>& self) {
.def_property_readonly("stride", [](const Containers::StridedArrayView<dimensions, T>& self) {
return stride<dimensions>(self.stride());
}, "View stride in each dimension")
.def_property_readonly("dimensions", [](const PyStridedArrayView<dimensions, T>&) { return dimensions; }, "Dimension count")
.def_readonly("obj", &PyStridedArrayView<dimensions, T>::obj, "Memory owner object")
.def_property_readonly("dimensions", [](const Containers::StridedArrayView<dimensions, T>&) { return dimensions; }, "Dimension count")
.def_property_readonly("obj", [](const Containers::StridedArrayView<dimensions, T>& self) {
return pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner;
}, "Memory owner object")
/* Conversion to bytes */
.def("__bytes__", [](const PyStridedArrayView<dimensions, T>& self) {
.def("__bytes__", [](const Containers::StridedArrayView<dimensions, T>& self) {
/* TODO: use _PyBytes_Resize() to avoid the double copy */
const Containers::Array<char> out = bytes(Containers::arrayCast<const char>(self));
return py::bytes(out.data(), out.size());
}, "Convert to bytes")
/* Slicing of the top dimension */
.def("__getitem__", [](const PyStridedArrayView<dimensions, T>& self, py::slice slice) -> py::object {
.def("__getitem__", [](const Containers::StridedArrayView<dimensions, T>& self, py::slice slice) {
const Slice calculated = calculateSlice(slice, Containers::StridedDimensions<dimensions, const std::size_t>{self.size()}[0]);
return py::cast(PyStridedArrayView<dimensions, T>(self.slice(calculated.start, calculated.stop).every(calculated.step), self.obj));
return Containers::pyArrayViewHolder(self.slice(calculated.start, calculated.stop).every(calculated.step), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
}, "Slice the view");
enableBetterBufferProtocol<PyStridedArrayView<dimensions, T>, stridedArrayViewBufferProtocol>(c);
enableBetterBufferProtocol<Containers::StridedArrayView<dimensions, T>, stridedArrayViewBufferProtocol>(c);
}
template<class T> void stridedArrayView1D(py::class_<PyStridedArrayView<1, T>>& c) {
template<class T> void stridedArrayView1D(py::class_<Containers::StridedArrayView<1, T>, Containers::PyArrayViewHolder<Containers::StridedArrayView<1, T>>>& c) {
c
/* Single item retrieval. Need to throw IndexError in order to allow
iteration: https://docs.python.org/3/reference/datamodel.html#object.__getitem__ */
.def("__getitem__", [](const PyStridedArrayView<1, T>& self, std::size_t i) {
.def("__getitem__", [](const Containers::StridedArrayView<1, T>& self, std::size_t i) {
if(i >= self.size()) throw pybind11::index_error{};
return self[i];
}, "Value at given position");
}
template<unsigned dimensions, class T> void stridedArrayViewND(py::class_<PyStridedArrayView<dimensions, T>>& c) {
template<unsigned dimensions, class T> void stridedArrayViewND(py::class_<Containers::StridedArrayView<dimensions, T>, Containers::PyArrayViewHolder<Containers::StridedArrayView<dimensions, T>>>& c) {
c
/* Sub-view retrieval. Need to throw IndexError in order to allow
iteration: https://docs.python.org/3/reference/datamodel.html#object.__getitem__ */
.def("__getitem__", [](const PyStridedArrayView<dimensions, T>& self, std::size_t i) {
.def("__getitem__", [](const Containers::StridedArrayView<dimensions, T>& self, std::size_t i) {
if(i >= Containers::StridedDimensions<dimensions, const std::size_t>{self.size()}[0]) throw pybind11::index_error{};
return PyStridedArrayView<dimensions - 1, T>{self[i], self.obj};
return Containers::pyArrayViewHolder(self[i], pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
}, "Sub-view at given position")
/* Single-item retrieval. Need to throw IndexError in order to allow
iteration: https://docs.python.org/3/reference/datamodel.html#object.__getitem__ */
/* Multi-dimensional slicing */
.def("__getitem__", [](const PyStridedArrayView<dimensions, T>& self, const typename DimensionsTuple<dimensions, py::slice>::Type& slice) -> py::object {
.def("__getitem__", [](const Containers::StridedArrayView<dimensions, T>& self, const typename DimensionsTuple<dimensions, py::slice>::Type& slice) {
Containers::StridedDimensions<dimensions, std::size_t> starts;
Containers::StridedDimensions<dimensions, std::size_t> stops;
Containers::StridedDimensions<dimensions, std::ptrdiff_t> steps;
@ -403,153 +407,153 @@ template<unsigned dimensions, class T> void stridedArrayViewND(py::class_<PyStri
steps[i] = calculated.step;
}
return py::cast(PyStridedArrayView<dimensions, T>(self.slice(starts, stops).every(steps), self.obj));
return Containers::pyArrayViewHolder(self.slice(starts, stops).every(steps), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
}, "Slice the view");
}
template<class T> void stridedArrayView2D(py::class_<PyStridedArrayView<2, T>>& c) {
template<class T> void stridedArrayView2D(py::class_<Containers::StridedArrayView<2, T>, Containers::PyArrayViewHolder<Containers::StridedArrayView<2, T>>>& c) {
c
.def("__getitem__", [](const PyStridedArrayView<2, T>& self, const std::tuple<std::size_t, std::size_t>& i) {
.def("__getitem__", [](const Containers::StridedArrayView<2, T>& self, const std::tuple<std::size_t, std::size_t>& i) {
if(std::get<0>(i) >= self.size()[0] ||
std::get<1>(i) >= self.size()[1]) throw py::index_error{};
return self[std::get<0>(i)][std::get<1>(i)];
}, "Value at given position")
.def("transposed", [](const PyStridedArrayView<2, T>& self, const std::size_t a, std::size_t b) {
.def("transposed", [](const Containers::StridedArrayView<2, T>& self, const std::size_t a, std::size_t b) {
if((a == 0 && b == 1) ||
(a == 1 && b == 0))
return PyStridedArrayView<2, T>{self.template transposed<0, 1>(), self.obj};
return Containers::pyArrayViewHolder(self.template transposed<0, 1>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
throw py::value_error{Utility::formatString("dimensions {}, {} can't be transposed in a {}D view", a, b, 2)};
}, "Transpose two dimensions")
.def("flipped", [](const PyStridedArrayView<2, T>& self, const std::size_t dimension) {
.def("flipped", [](const Containers::StridedArrayView<2, T>& self, const std::size_t dimension) {
if(dimension == 0)
return PyStridedArrayView<2, T>{self.template flipped<0>(), self.obj};
return Containers::pyArrayViewHolder(self.template flipped<0>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if(dimension == 1)
return PyStridedArrayView<2, T>{self.template flipped<1>(), self.obj};
return Containers::pyArrayViewHolder(self.template flipped<1>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
throw py::value_error{Utility::formatString("dimension {} out of range for a {}D view", dimension, 2)};
}, "Flip a dimension")
.def("broadcasted", [](const PyStridedArrayView<2, T>& self, const std::size_t dimension, std::size_t size) {
.def("broadcasted", [](const Containers::StridedArrayView<2, T>& self, const std::size_t dimension, std::size_t size) {
if(dimension == 0)
return PyStridedArrayView<2, T>{self.template broadcasted<0>(size), self.obj};
return Containers::pyArrayViewHolder(self.template broadcasted<0>(size), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if(dimension == 1)
return PyStridedArrayView<2, T>{self.template broadcasted<1>(size), self.obj};
return Containers::pyArrayViewHolder(self.template broadcasted<1>(size), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
throw py::value_error{Utility::formatString("dimension {} out of range for a {}D view", dimension, 2)};
}, "Broadcast a dimension");
}
template<class T> void stridedArrayView3D(py::class_<PyStridedArrayView<3, T>>& c) {
template<class T> void stridedArrayView3D(py::class_<Containers::StridedArrayView<3, T>, Containers::PyArrayViewHolder<Containers::StridedArrayView<3, T>>>& c) {
c
.def("__getitem__", [](const PyStridedArrayView<3, T>& self, const std::tuple<std::size_t, std::size_t, std::size_t>& i) {
.def("__getitem__", [](const Containers::StridedArrayView<3, T>& self, const std::tuple<std::size_t, std::size_t, std::size_t>& i) {
if(std::get<0>(i) >= self.size()[0] ||
std::get<1>(i) >= self.size()[1] ||
std::get<2>(i) >= self.size()[2]) throw pybind11::index_error{};
return self[std::get<0>(i)][std::get<1>(i)][std::get<2>(i)];
}, "Value at given position")
.def("transposed", [](const PyStridedArrayView<3, T>& self, const std::size_t a, std::size_t b) {
.def("transposed", [](const Containers::StridedArrayView<3, T>& self, const std::size_t a, std::size_t b) {
if((a == 0 && b == 1) ||
(a == 1 && b == 0))
return PyStridedArrayView<3, T>{self.template transposed<0, 1>(), self.obj};
return Containers::pyArrayViewHolder(self.template transposed<0, 1>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if((a == 0 && b == 2) ||
(a == 2 && b == 0))
return PyStridedArrayView<3, T>{self.template transposed<0, 2>(), self.obj};
return Containers::pyArrayViewHolder(self.template transposed<0, 2>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if((a == 1 && b == 2) ||
(a == 2 && b == 1))
return PyStridedArrayView<3, T>{self.template transposed<1, 2>(), self.obj};
return Containers::pyArrayViewHolder(self.template transposed<1, 2>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
throw py::value_error{Utility::formatString("dimensions {}, {} can't be transposed in a {}D view", a, b, 3)};
}, "Transpose two dimensions")
.def("flipped", [](const PyStridedArrayView<3, T>& self, const std::size_t dimension) {
.def("flipped", [](const Containers::StridedArrayView<3, T>& self, const std::size_t dimension) {
if(dimension == 0)
return PyStridedArrayView<3, T>{self.template flipped<0>(), self.obj};
return Containers::pyArrayViewHolder(self.template flipped<0>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if(dimension == 1)
return PyStridedArrayView<3, T>{self.template flipped<1>(), self.obj};
return Containers::pyArrayViewHolder(self.template flipped<1>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if(dimension == 2)
return PyStridedArrayView<3, T>{self.template flipped<2>(), self.obj};
return Containers::pyArrayViewHolder(self.template flipped<2>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
throw py::value_error{Utility::formatString("dimension {} out of range for a {}D view", dimension, 3)};
}, "Flip a dimension")
.def("broadcasted", [](const PyStridedArrayView<3, T>& self, const std::size_t dimension, std::size_t size) {
.def("broadcasted", [](const Containers::StridedArrayView<3, T>& self, const std::size_t dimension, std::size_t size) {
if(dimension == 0)
return PyStridedArrayView<3, T>{self.template broadcasted<0>(size), self.obj};
return Containers::pyArrayViewHolder(self.template broadcasted<0>(size), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if(dimension == 1)
return PyStridedArrayView<3, T>{self.template broadcasted<1>(size), self.obj};
return Containers::pyArrayViewHolder(self.template broadcasted<1>(size), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if(dimension == 2)
return PyStridedArrayView<3, T>{self.template broadcasted<2>(size), self.obj};
return Containers::pyArrayViewHolder(self.template broadcasted<2>(size), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
throw py::value_error{Utility::formatString("dimension {} out of range for a {}D view", dimension, 3)};
}, "Broadcast a dimension");
}
template<class T> void stridedArrayView4D(py::class_<PyStridedArrayView<4, T>>& c) {
template<class T> void stridedArrayView4D(py::class_<Containers::StridedArrayView<4, T>, Containers::PyArrayViewHolder<Containers::StridedArrayView<4, T>>>& c) {
c
.def("__getitem__", [](const PyStridedArrayView<4, T>& self, const std::tuple<std::size_t, std::size_t, std::size_t, std::size_t>& i) {
.def("__getitem__", [](const Containers::StridedArrayView<4, T>& self, const std::tuple<std::size_t, std::size_t, std::size_t, std::size_t>& i) {
if(std::get<0>(i) >= self.size()[0] ||
std::get<1>(i) >= self.size()[1] ||
std::get<2>(i) >= self.size()[2] ||
std::get<3>(i) >= self.size()[3]) throw pybind11::index_error{};
return self[std::get<0>(i)][std::get<1>(i)][std::get<2>(i)][std::get<3>(i)];
}, "Value at given position")
.def("transposed", [](const PyStridedArrayView<4, T>& self, const std::size_t a, std::size_t b) {
.def("transposed", [](const Containers::StridedArrayView<4, T>& self, const std::size_t a, std::size_t b) {
if((a == 0 && b == 1) ||
(a == 1 && b == 0))
return PyStridedArrayView<4, T>{self.template transposed<0, 1>(), self.obj};
return Containers::pyArrayViewHolder(self.template transposed<0, 1>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if((a == 0 && b == 2) ||
(a == 2 && b == 0))
return PyStridedArrayView<4, T>{self.template transposed<0, 2>(), self.obj};
return Containers::pyArrayViewHolder(self.template transposed<0, 2>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if((a == 0 && b == 3) ||
(a == 3 && b == 0))
return PyStridedArrayView<4, T>{self.template transposed<0, 3>(), self.obj};
return Containers::pyArrayViewHolder(self.template transposed<0, 3>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if((a == 1 && b == 2) ||
(a == 2 && b == 1))
return PyStridedArrayView<4, T>{self.template transposed<1, 2>(), self.obj};
return Containers::pyArrayViewHolder(self.template transposed<1, 2>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if((a == 1 && b == 3) ||
(a == 3 && b == 1))
return PyStridedArrayView<4, T>{self.template transposed<1, 3>(), self.obj};
return Containers::pyArrayViewHolder(self.template transposed<1, 3>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if((a == 2 && b == 3) ||
(a == 3 && b == 2))
return PyStridedArrayView<4, T>{self.template transposed<2, 3>(), self.obj};
return Containers::pyArrayViewHolder(self.template transposed<2, 3>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
throw py::value_error{Utility::formatString("dimensions {}, {} can't be transposed in a {}D view", a, b, 4)};
}, "Transpose two dimensions")
.def("flipped", [](const PyStridedArrayView<4, T>& self, const std::size_t dimension) {
.def("flipped", [](const Containers::StridedArrayView<4, T>& self, const std::size_t dimension) {
if(dimension == 0)
return PyStridedArrayView<4, T>{self.template flipped<0>(), self.obj};
return Containers::pyArrayViewHolder(self.template flipped<0>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if(dimension == 1)
return PyStridedArrayView<4, T>{self.template flipped<1>(), self.obj};
return Containers::pyArrayViewHolder(self.template flipped<1>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if(dimension == 2)
return PyStridedArrayView<4, T>{self.template flipped<2>(), self.obj};
return Containers::pyArrayViewHolder(self.template flipped<2>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if(dimension == 3)
return PyStridedArrayView<4, T>{self.template flipped<3>(), self.obj};
return Containers::pyArrayViewHolder(self.template flipped<3>(), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
throw py::value_error{Utility::formatString("dimension {} out of range for a {}D view", dimension, 4)};
}, "Flip a dimension")
.def("broadcasted", [](const PyStridedArrayView<4, T>& self, const std::size_t dimension, std::size_t size) {
.def("broadcasted", [](const Containers::StridedArrayView<4, T>& self, const std::size_t dimension, std::size_t size) {
if(dimension == 0)
return PyStridedArrayView<4, T>{self.template broadcasted<0>(size), self.obj};
return Containers::pyArrayViewHolder(self.template broadcasted<0>(size), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if(dimension == 1)
return PyStridedArrayView<4, T>{self.template broadcasted<1>(size), self.obj};
return Containers::pyArrayViewHolder(self.template broadcasted<1>(size), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if(dimension == 2)
return PyStridedArrayView<4, T>{self.template broadcasted<2>(size), self.obj};
return Containers::pyArrayViewHolder(self.template broadcasted<2>(size), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
if(dimension == 3)
return PyStridedArrayView<4, T>{self.template broadcasted<3>(size), self.obj};
return Containers::pyArrayViewHolder(self.template broadcasted<3>(size), pyObjectHolderFor<Containers::PyArrayViewHolder>(self).owner);
throw py::value_error{Utility::formatString("dimension {} out of range for a {}D view", dimension, 4)};
}, "Broadcast a dimension");
}
template<class T> void mutableStridedArrayView1D(py::class_<PyStridedArrayView<1, T>>& c) {
template<class T> void mutableStridedArrayView1D(py::class_<Containers::StridedArrayView<1, T>, Containers::PyArrayViewHolder<Containers::StridedArrayView<1, T>>>& c) {
c
.def("__setitem__", [](const PyStridedArrayView<1, T>& self, const std::size_t i, const T& value) {
.def("__setitem__", [](const Containers::StridedArrayView<1, T>& self, const std::size_t i, const T& value) {
if(i >= self.size()) throw pybind11::index_error{};
self[i] = value;
}, "Set a value at given position");
}
template<class T> void mutableStridedArrayView2D(py::class_<PyStridedArrayView<2, T>>& c) {
template<class T> void mutableStridedArrayView2D(py::class_<Containers::StridedArrayView<2, T>, Containers::PyArrayViewHolder<Containers::StridedArrayView<2, T>>>& c) {
c
.def("__setitem__", [](const PyStridedArrayView<2, T>& self, const std::tuple<std::size_t, std::size_t>& i, const T& value) {
.def("__setitem__", [](const Containers::StridedArrayView<2, T>& self, const std::tuple<std::size_t, std::size_t>& i, const T& value) {
if(std::get<0>(i) >= self.size()[0] ||
std::get<1>(i) >= self.size()[1]) throw pybind11::index_error{};
self[std::get<0>(i)][std::get<1>(i)] = value;
}, "Set a value at given position");
}
template<class T> void mutableStridedArrayView3D(py::class_<PyStridedArrayView<3, T>>& c) {
template<class T> void mutableStridedArrayView3D(py::class_<Containers::StridedArrayView<3, T>, Containers::PyArrayViewHolder<Containers::StridedArrayView<3, T>>>& c) {
c
.def("__setitem__", [](const PyStridedArrayView<3, T>& self, const std::tuple<std::size_t, std::size_t, std::size_t>& i, const T& value) {
.def("__setitem__", [](const Containers::StridedArrayView<3, T>& self, const std::tuple<std::size_t, std::size_t, std::size_t>& i, const T& value) {
if(std::get<0>(i) >= self.size()[0] ||
std::get<1>(i) >= self.size()[1] ||
std::get<2>(i) >= self.size()[2]) throw pybind11::index_error{};
@ -557,9 +561,9 @@ template<class T> void mutableStridedArrayView3D(py::class_<PyStridedArrayView<3
}, "Set a value at given position");
}
template<class T> void mutableStridedArrayView4D(py::class_<PyStridedArrayView<4, T>>& c) {
template<class T> void mutableStridedArrayView4D(py::class_<Containers::StridedArrayView<4, T>, Containers::PyArrayViewHolder<Containers::StridedArrayView<4, T>>>& c) {
c
.def("__setitem__", [](const PyStridedArrayView<4, T>& self, const std::tuple<std::size_t, std::size_t, std::size_t, std::size_t>& i, const T& value) {
.def("__setitem__", [](const Containers::StridedArrayView<4, T>& self, const std::tuple<std::size_t, std::size_t, std::size_t, std::size_t>& i, const T& value) {
if(std::get<0>(i) >= self.size()[0] ||
std::get<1>(i) >= self.size()[1] ||
std::get<2>(i) >= self.size()[2] ||
@ -573,22 +577,22 @@ template<class T> void mutableStridedArrayView4D(py::class_<PyStridedArrayView<4
void containers(py::module& m) {
m.doc() = "Corrade containers module";
py::class_<PyArrayView<const char>> arrayView_{m,
py::class_<Containers::ArrayView<const char>, Containers::PyArrayViewHolder<Containers::ArrayView<const char>>> arrayView_{m,
"ArrayView", "Array view", py::buffer_protocol{}};
arrayView(arrayView_);
py::class_<PyArrayView<char>> mutableArrayView_{m,
py::class_<Containers::ArrayView<char>, Containers::PyArrayViewHolder<Containers::ArrayView<char>>> mutableArrayView_{m,
"MutableArrayView", "Mutable array view", py::buffer_protocol{}};
arrayView(mutableArrayView_);
mutableArrayView(mutableArrayView_);
py::class_<PyStridedArrayView<1, const char>> stridedArrayView1D_{m,
py::class_<Containers::StridedArrayView<1, const char>, Containers::PyArrayViewHolder<Containers::StridedArrayView<1, const char>>> stridedArrayView1D_{m,
"StridedArrayView1D", "One-dimensional array view with stride information", py::buffer_protocol{}};
py::class_<PyStridedArrayView<2, const char>> stridedArrayView2D_{m,
py::class_<Containers::StridedArrayView<2, const char>, Containers::PyArrayViewHolder<Containers::StridedArrayView<2, const char>>> stridedArrayView2D_{m,
"StridedArrayView2D", "Two-dimensional array view with stride information", py::buffer_protocol{}};
py::class_<PyStridedArrayView<3, const char>> stridedArrayView3D_{m,
py::class_<Containers::StridedArrayView<3, const char>, Containers::PyArrayViewHolder<Containers::StridedArrayView<3, const char>>> stridedArrayView3D_{m,
"StridedArrayView3D", "Three-dimensional array view with stride information", py::buffer_protocol{}};
py::class_<PyStridedArrayView<4, const char>> stridedArrayView4D_{m,
py::class_<Containers::StridedArrayView<4, const char>, Containers::PyArrayViewHolder<Containers::StridedArrayView<4, const char>>> stridedArrayView4D_{m,
"StridedArrayView4D", "Four-dimensional array view with stride information", py::buffer_protocol{}};
stridedArrayView(stridedArrayView1D_);
stridedArrayView1D(stridedArrayView1D_);
@ -602,13 +606,13 @@ void containers(py::module& m) {
stridedArrayViewND(stridedArrayView4D_);
stridedArrayView4D(stridedArrayView4D_);
py::class_<PyStridedArrayView<1, char>> mutableStridedArrayView1D_{m,
py::class_<Containers::StridedArrayView<1, char>, Containers::PyArrayViewHolder<Containers::StridedArrayView<1, char>>> mutableStridedArrayView1D_{m,
"MutableStridedArrayView1D", "Mutable one-dimensional array view with stride information", py::buffer_protocol{}};
py::class_<PyStridedArrayView<2, char>> mutableStridedArrayView2D_{m,
py::class_<Containers::StridedArrayView<2, char>, Containers::PyArrayViewHolder<Containers::StridedArrayView<2, char>>> mutableStridedArrayView2D_{m,
"MutableStridedArrayView2D", "Mutable two-dimensional array view with stride information", py::buffer_protocol{}};
py::class_<PyStridedArrayView<3, char>> mutableStridedArrayView3D_{m,
py::class_<Containers::StridedArrayView<3, char>, Containers::PyArrayViewHolder<Containers::StridedArrayView<3, char>>> mutableStridedArrayView3D_{m,
"MutableStridedArrayView3D", "Mutable three-dimensional array view with stride information", py::buffer_protocol{}};
py::class_<PyStridedArrayView<4, char>> mutableStridedArrayView4D_{m,
py::class_<Containers::StridedArrayView<4, char>, Containers::PyArrayViewHolder<Containers::StridedArrayView<4, char>>> mutableStridedArrayView4D_{m,
"MutableStridedArrayView4D", "Mutable four-dimensional array view with stride information", py::buffer_protocol{}};
stridedArrayView(mutableStridedArrayView1D_);
stridedArrayView1D(mutableStridedArrayView1D_);

8
src/python/magnum/gl.cpp

@ -42,7 +42,6 @@
#include "Corrade/Python.h"
#include "Magnum/Python.h"
#include "corrade/PyArrayView.h"
#include "corrade/EnumOperators.h"
#include "magnum/bootstrap.h"
#include "magnum/NonDestructible.h"
@ -177,7 +176,7 @@ void gl(py::module& m) {
.def_property_readonly("id", &GL::Buffer::id, "OpenGL buffer ID")
.def_property("target_hint", &GL::Buffer::targetHint, &GL::Buffer::setTargetHint, "Target hint")
/* Using lambdas to avoid method chaining getting into signatures */
.def("set_data", [](GL::Buffer& self, const corrade::PyArrayView<const char>& data, GL::BufferUsage usage) {
.def("set_data", [](GL::Buffer& self, const Containers::ArrayView<const char>& data, GL::BufferUsage usage) {
self.setData(data, usage);
}, "Set buffer data", py::arg("data"), py::arg("usage") = GL::BufferUsage::StaticDraw)
/** @todo more */;
@ -300,9 +299,8 @@ void gl(py::module& m) {
.def("clear", [](GL::AbstractFramebuffer& self, GL::FramebufferClear mask) {
self.clear(mask);
}, "Clear specified buffers in the framebuffer")
.def("read", [](GL::AbstractFramebuffer& self, const Range2Di& rectangle, PyImageView<2, char>& image) {
self.read(rectangle, image);
}, "Read block of pixels from the framebuffer to an image view");
.def("read", static_cast<void(GL::AbstractFramebuffer::*)(const Range2Di&, const MutableImageView2D&)>(&GL::AbstractFramebuffer::read),
"Read block of pixels from the framebuffer to an image view");
NonDestructibleBase<GL::DefaultFramebuffer, GL::AbstractFramebuffer> defaultFramebuffer{m,
"DefaultFramebuffer", "Default framebuffer"};

50
src/python/magnum/magnum.cpp

@ -25,13 +25,16 @@
#include <pybind11/operators.h>
#include <pybind11/pybind11.h>
#include <Corrade/Containers/StridedArrayView.h>
#include <Magnum/ImageView.h>
#include <Magnum/Mesh.h>
#include <Magnum/PixelFormat.h>
#include <Magnum/PixelStorage.h>
#include "Corrade/Python.h"
#include "Corrade/Containers/Python.h"
#include "Magnum/Python.h"
#include "corrade/PyArrayView.h"
#include "magnum/bootstrap.h"
#ifdef MAGNUM_BUILD_STATIC
@ -56,7 +59,7 @@ template<class T> struct PyDimensionTraits<3, T> {
static VectorType from(const Math::Vector<3, T>& vec) { return vec; }
};
template<class T> void imageView(py::class_<T>& c) {
template<class T> void imageView(py::class_<T, PyImageViewHolder<T>>& c) {
/*
Missing APIs:
@ -65,17 +68,17 @@ template<class T> void imageView(py::class_<T>& c) {
c
/* Constructors */
.def(py::init([](const PixelStorage& storage, PixelFormat format, const typename PyDimensionTraits<T::Dimensions, Int>::VectorType& size, const corrade::PyArrayView<typename T::Type>& data) {
return T{ImageView<T::Dimensions, typename T::Type>{storage, format, size, data}, data.obj};
.def(py::init([](const PixelStorage& storage, PixelFormat format, const typename PyDimensionTraits<T::Dimensions, Int>::VectorType& size, const Containers::ArrayView<typename T::Type>& data) {
return pyImageViewHolder(T{storage, format, size, data}, pyObjectHolderFor<Containers::PyArrayViewHolder>(data).owner);
}), "Constructor")
.def(py::init([](PixelFormat format, const typename PyDimensionTraits<T::Dimensions, Int>::VectorType& size, const corrade::PyArrayView<typename T::Type>& data) {
return T{ImageView<T::Dimensions, typename T::Type>{format, size, data}, data.obj};
.def(py::init([](PixelFormat format, const typename PyDimensionTraits<T::Dimensions, Int>::VectorType& size, const Containers::ArrayView<typename T::Type>& data) {
return pyImageViewHolder(T{format, size, data}, pyObjectHolderFor<Containers::PyArrayViewHolder>(data).owner);
}), "Constructor")
.def(py::init([](const PixelStorage& storage, PixelFormat format, const typename PyDimensionTraits<T::Dimensions, Int>::VectorType& size) {
return T{ImageView<T::Dimensions, typename T::Type>{storage, format, size}, py::none{}};
return T{storage, format, size};
}), "Construct an empty view")
.def(py::init([](PixelFormat format, const typename PyDimensionTraits<T::Dimensions, Int>::VectorType& size) {
return T{ImageView<T::Dimensions, typename T::Type>{format, size}, py::none{}};
return T{format, size};
}), "Construct an empty view")
/* Properties */
@ -86,22 +89,25 @@ template<class T> void imageView(py::class_<T>& c) {
return PyDimensionTraits<T::Dimensions, Int>::from(self.size());
}, "Image size")
.def_property("data", [](T& self) {
return corrade::PyArrayView<typename T::Type>{{static_cast<typename T::Type*>(self.data().data()), self.data().size()}, self.owner};
}, [](T& self, const corrade::PyArrayView<typename T::Type>& data) {
return Containers::pyArrayViewHolder(self.data(), pyObjectHolderFor<PyImageViewHolder>(self).owner);
}, [](T& self, const Containers::ArrayView<typename T::Type>& data) {
self.setData(data);
self.owner = data.obj;
pyObjectHolderFor<PyImageViewHolder>(self).owner =
pyObjectHolderFor<Containers::PyArrayViewHolder>(data).owner;
}, "Image data")
.def_property_readonly("pixels", [](T& self) {
return corrade::PyStridedArrayView<T::Dimensions+1, typename T::Type>{self.pixels(), self.owner};
return Containers::pyArrayViewHolder(self.pixels(), pyObjectHolderFor<PyImageViewHolder>(self).owner);
}, "View on pixel data")
.def_readonly("owner", &T::owner, "Memory owner");
.def_property_readonly("owner", [](T& self) {
return pyObjectHolderFor<PyImageViewHolder>(self).owner;
}, "Memory owner");
}
template<class T> void imageViewFromMutable(py::class_<T>& c) {
template<class T> void imageViewFromMutable(py::class_<T, PyImageViewHolder<T>>& c) {
c
.def(py::init([](const PyImageView<T::Dimensions, char>& other) {
return T{ImageView<T::Dimensions, const char>{other}, other.owner};
.def(py::init([](const BasicMutableImageView<T::Dimensions>& other) {
return pyImageViewHolder(BasicImageView<T::Dimensions>(other), pyObjectHolderFor<PyImageViewHolder>(other).owner);
}), "Constructor");
}
@ -187,12 +193,12 @@ void magnum(py::module& m) {
.def_property("skip",
&PixelStorage::skip, &PixelStorage::setSkip, "Pixel, row and image skip");
py::class_<PyImageView<1, const char>> imageView1D{m, "ImageView1D", "One-dimensional image view"};
py::class_<PyImageView<2, const char>> imageView2D{m, "ImageView2D", "Two-dimensional image view"};
py::class_<PyImageView<3, const char>> imageView3D{m, "ImageView3D", "Three-dimensional image view"};
py::class_<PyImageView<1, char>> mutableImageView1D{m, "MutableImageView1D", "One-dimensional mutable image view"};
py::class_<PyImageView<2, char>> mutableImageView2D{m, "MutableImageView2D", "Two-dimensional mutable image view"};
py::class_<PyImageView<3, char>> mutableImageView3D{m, "MutableImageView3D", "Three-dimensional mutable image view"};
py::class_<ImageView1D, PyImageViewHolder<ImageView1D>> imageView1D{m, "ImageView1D", "One-dimensional image view"};
py::class_<ImageView2D, PyImageViewHolder<ImageView2D>> imageView2D{m, "ImageView2D", "Two-dimensional image view"};
py::class_<ImageView3D, PyImageViewHolder<ImageView3D>> imageView3D{m, "ImageView3D", "Three-dimensional image view"};
py::class_<MutableImageView1D, PyImageViewHolder<MutableImageView1D>> mutableImageView1D{m, "MutableImageView1D", "One-dimensional mutable image view"};
py::class_<MutableImageView2D, PyImageViewHolder<MutableImageView2D>> mutableImageView2D{m, "MutableImageView2D", "Two-dimensional mutable image view"};
py::class_<MutableImageView3D, PyImageViewHolder<MutableImageView3D>> mutableImageView3D{m, "MutableImageView3D", "Three-dimensional mutable image view"};
imageView(imageView1D);
imageView(imageView2D);

Loading…
Cancel
Save