Unify pybind11 and nanobind into single-source bindings (#5254)
Exploring an idea Anton had about unifiying two python backends into
single source since they are so similar.
## Summary
This PR unifies the early nanobind migration with the existing pybind11
Python bindings by compiling **shared sources** from `src/python/` for
both backends, instead of maintaining parallel copies under
`src/python-nanobind/`.
Migrated modules so far: `ROI`, `TypeDesc`, `ImageSpec`, `ParamValue` /
`ParamValueList`, plus the core `OpenImageIO` module helpers in
`py_oiio.cpp`. Backend differences are isolated in `py_backend.h` (type
aliases, binding API macros like `.OIIO_PY_RW(...)`, and small
`oiio_py::` helpers). Shared Python↔C++ conversion logic lives in
`py_oiio.h` with `#if` only where the backends genuinely differ.
CMake is updated so that when `-DOIIO_PYTHON_BINDINGS_BACKEND=nanobind`
is selected, the module is built from the unified sources and installed
as the main `OpenImageIO` package in the expected location. With `both`,
pybind11 remains the primary module and nanobind builds as
`PyOpenImageIONanobind` for side-by-side testing.
Duplicate nanobind-only `.cpp` files are removed (~1.8k lines deleted
net). Testsuite coverage is extended with explicit roundtrip checks for
shared helper paths (attributes, buffers, tuples).
---
## Pros
- **Single source of truth** for migrated bindings — no more keeping two
copies of `py_roi.cpp`, `py_imagespec.cpp`, etc. in sync by hand.
- **Lower risk of backend drift** — a fix or API tweak in a migrated
module automatically applies to both pybind11 and nanobind.
- **Net code reduction** — removes a large duplicated tree under
`src/python-nanobind/` while centralizing shared logic in one place.
- **Clearer migration path** — `MIGRATION_STATUS.md` documents what is
fully migrated, partially migrated, and still pybind-only.
- **Better nanobind-only build layout** — on `main`, choosing nanobind
does not install into the same package layout as pybind11; this PR fixes
that for the migrated surface.
- **Side-by-side validation** — `OIIO_PYTHON_BINDINGS_BACKEND=both`
builds both modules so existing `testsuite/python-*` and `*.nanobind`
variants can be run against the same C++ sources.
- **Explicit parity tests** — new/extended tests cover shared conversion
paths (typed attributes, buffer→vector, tuple roundtrips) rather than
relying on import smoke tests alone.
- **Maintainable binding style** — method-name macros
(`.OIIO_PY_RW(...)`) keep clang-format happy without `clang-format off`
regions.
---
## Cons
- **Added abstraction in the pybind11 path** — even pybind-only builds
now go through `py_backend.h`, macros, and shared helpers; slightly more
indirection than plain `.def_readwrite(...)`.
- **Regression risk for pybind11** — adapting existing pybind code to
work for both backends could subtly change pybind11 behavior at the
edges (init, casting, error paths, ownership).
- **Incomplete migration** — most of the Python API (`ImageBuf`,
`ImageInput`/`ImageOutput`, `ImageBufAlgo`, texture system, etc.) is
still pybind-only; this PR is a foundation, not full nanobind coverage.
- **Known nanobind gaps remain** — module-level exports and helpers
listed in `MIGRATION_STATUS.md` are still missing or intentionally
different vs pybind11.
- **Higher review and test burden** — reviewers need to think about two
backends; CI/local testing should cover both pybind and `.nanobind`
variants for touched code.
- **Conditional compilation complexity** — `#if
defined(OIIO_PY_BACKEND_NANOBIND)` remains in a few places (e.g.
`ParamValue` constructors, buffer dispatch); easy to get wrong if
extended carelessly.
- **Slightly less obvious binding code** — `.OIIO_PY_RW("x", ...)` reads
less directly than `.def_readwrite("x", ...)` until you know the macro
map.
---------
Signed-off-by: Aleksandr Motsjonov <soswow@gmail.com>
This commit is contained in:
+4
-2
@@ -314,10 +314,12 @@ else ()
|
||||
set (_py_dev_found Python3_Development.Module_FOUND)
|
||||
endif ()
|
||||
if (USE_PYTHON AND ${_py_dev_found} AND NOT BUILD_OIIOUTIL_ONLY)
|
||||
if (OIIO_BUILD_PYTHON_PYBIND11)
|
||||
if (OIIO_BUILD_PYTHON_PYBIND11
|
||||
OR OIIO_PYTHON_BINDINGS_BACKEND STREQUAL "nanobind")
|
||||
add_subdirectory (src/python)
|
||||
endif ()
|
||||
if (OIIO_BUILD_PYTHON_NANOBIND)
|
||||
if (OIIO_BUILD_PYTHON_NANOBIND
|
||||
AND OIIO_PYTHON_BINDINGS_BACKEND STREQUAL "both")
|
||||
add_subdirectory (src/python-nanobind)
|
||||
endif ()
|
||||
else ()
|
||||
|
||||
+37
-18
@@ -263,25 +263,44 @@ macro (setup_python_module_nanobind)
|
||||
OUTPUT_NAME ${lib_MODULE}
|
||||
DEBUG_POSTFIX "")
|
||||
|
||||
if (SKBUILD)
|
||||
set (_nanobind_install_dir .)
|
||||
if (OIIO_PYTHON_BINDINGS_BACKEND STREQUAL "both")
|
||||
if (SKBUILD)
|
||||
set (_nanobind_install_dir .)
|
||||
else ()
|
||||
set (_nanobind_install_dir ${PYTHON_SITE_DIR})
|
||||
endif ()
|
||||
|
||||
# Keep nanobind modules isolated in the build tree so they don't alter
|
||||
# how the existing top-level OpenImageIO module is imported during tests.
|
||||
set_target_properties (${target_name} PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/python/nanobind/OpenImageIO
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/python/nanobind/OpenImageIO
|
||||
)
|
||||
|
||||
install (TARGETS ${target_name}
|
||||
RUNTIME DESTINATION ${_nanobind_install_dir} COMPONENT user
|
||||
LIBRARY DESTINATION ${_nanobind_install_dir} COMPONENT user)
|
||||
|
||||
if (lib_PACKAGE_FILES)
|
||||
install (FILES ${lib_PACKAGE_FILES}
|
||||
DESTINATION ${_nanobind_install_dir} COMPONENT user)
|
||||
endif ()
|
||||
else ()
|
||||
set (_nanobind_install_dir ${PYTHON_SITE_DIR})
|
||||
if (SKBUILD)
|
||||
set (PYTHON_SITE_DIR .)
|
||||
endif ()
|
||||
|
||||
set_target_properties (${target_name} PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/python/site-packages
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/python/site-packages
|
||||
)
|
||||
|
||||
install (TARGETS ${target_name}
|
||||
RUNTIME DESTINATION ${PYTHON_SITE_DIR} COMPONENT user
|
||||
LIBRARY DESTINATION ${PYTHON_SITE_DIR} COMPONENT user)
|
||||
|
||||
install (FILES __init__.py stubs/OpenImageIO/__init__.pyi stubs/OpenImageIO/py.typed
|
||||
DESTINATION ${PYTHON_SITE_DIR} COMPONENT user)
|
||||
endif ()
|
||||
|
||||
# Keep nanobind modules isolated in the build tree so they don't alter
|
||||
# how the existing top-level OpenImageIO module is imported during tests.
|
||||
set_target_properties (${target_name} PROPERTIES
|
||||
LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/python/nanobind/OpenImageIO
|
||||
ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib/python/nanobind/OpenImageIO
|
||||
)
|
||||
|
||||
install (TARGETS ${target_name}
|
||||
RUNTIME DESTINATION ${_nanobind_install_dir} COMPONENT user
|
||||
LIBRARY DESTINATION ${_nanobind_install_dir} COMPONENT user)
|
||||
|
||||
if (lib_PACKAGE_FILES)
|
||||
install (FILES ${lib_PACKAGE_FILES}
|
||||
DESTINATION ${_nanobind_install_dir} COMPONENT user)
|
||||
endif ()
|
||||
endmacro ()
|
||||
|
||||
@@ -3,11 +3,11 @@
|
||||
# https://github.com/AcademySoftwareFoundation/OpenImageIO
|
||||
|
||||
set (nanobind_srcs
|
||||
py_oiio.cpp
|
||||
py_paramvalue.cpp
|
||||
py_roi.cpp
|
||||
py_imagespec.cpp
|
||||
py_typedesc.cpp)
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../python/py_oiio.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../python/py_paramvalue.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../python/py_roi.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../python/py_imagespec.cpp
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/../python/py_typedesc.cpp)
|
||||
|
||||
set (nanobind_build_package_dir ${CMAKE_BINARY_DIR}/lib/python/nanobind/OpenImageIO)
|
||||
file (MAKE_DIRECTORY ${nanobind_build_package_dir})
|
||||
@@ -20,13 +20,11 @@ setup_python_module_nanobind (
|
||||
MODULE _OpenImageIO
|
||||
SOURCES ${nanobind_srcs}
|
||||
LIBS OpenImageIO
|
||||
PACKAGE_FILES __init__.py
|
||||
)
|
||||
|
||||
if (OIIO_PYTHON_BINDINGS_BACKEND STREQUAL "nanobind")
|
||||
if (SKBUILD)
|
||||
install (FILES __init__.py DESTINATION . COMPONENT user)
|
||||
else ()
|
||||
install (FILES __init__.py
|
||||
DESTINATION ${PYTHON_SITE_DIR} COMPONENT user)
|
||||
endif ()
|
||||
endif ()
|
||||
target_compile_definitions (PyOpenImageIONanobind
|
||||
PRIVATE OIIO_PY_BACKEND_NANOBIND
|
||||
OIIO_PY_NANOBIND_ISOLATED_PACKAGE)
|
||||
target_include_directories (PyOpenImageIONanobind
|
||||
PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../python)
|
||||
|
||||
@@ -1,290 +0,0 @@
|
||||
// Copyright Contributors to the OpenImageIO project.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// https://github.com/AcademySoftwareFoundation/OpenImageIO
|
||||
|
||||
#include "py_oiio.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace OIIO;
|
||||
|
||||
nb::tuple
|
||||
imagespec_get_channelformats(const ImageSpec& spec, bool allow_empty = true)
|
||||
{
|
||||
std::vector<TypeDesc> formats;
|
||||
if (spec.channelformats.size() || !allow_empty)
|
||||
spec.get_channelformats(formats);
|
||||
return PyOpenImageIO::C_to_tuple(cspan<TypeDesc>(formats));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
imagespec_set_channelformats(ImageSpec& spec, nb::handle py_channelformats)
|
||||
{
|
||||
spec.channelformats.clear();
|
||||
PyOpenImageIO::py_to_stdvector(spec.channelformats, py_channelformats);
|
||||
}
|
||||
|
||||
|
||||
nb::tuple
|
||||
imagespec_get_channelnames(const ImageSpec& spec)
|
||||
{
|
||||
return PyOpenImageIO::C_to_tuple(cspan<std::string>(spec.channelnames));
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
imagespec_set_channelnames(ImageSpec& spec, nb::handle py_channelnames)
|
||||
{
|
||||
spec.channelnames.clear();
|
||||
PyOpenImageIO::py_to_stdvector(spec.channelnames, py_channelnames);
|
||||
}
|
||||
|
||||
|
||||
nb::object
|
||||
imagespec_getattribute_typed(const ImageSpec& spec, const std::string& name,
|
||||
TypeDesc type = TypeUnknown)
|
||||
{
|
||||
ParamValue tmpparam;
|
||||
const ParamValue* p = spec.find_attribute(name, tmpparam, type);
|
||||
if (!p)
|
||||
return nb::none();
|
||||
return PyOpenImageIO::make_pyobject(p->data(), p->type(), p->nvalues());
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
namespace PyOpenImageIO {
|
||||
|
||||
void
|
||||
declare_imagespec(nb::module_& m)
|
||||
{
|
||||
nb::class_<ImageSpec>(m, "ImageSpec")
|
||||
.def_rw("x", &ImageSpec::x)
|
||||
.def_rw("y", &ImageSpec::y)
|
||||
.def_rw("z", &ImageSpec::z)
|
||||
.def_rw("width", &ImageSpec::width)
|
||||
.def_rw("height", &ImageSpec::height)
|
||||
.def_rw("depth", &ImageSpec::depth)
|
||||
.def_rw("full_x", &ImageSpec::full_x)
|
||||
.def_rw("full_y", &ImageSpec::full_y)
|
||||
.def_rw("full_z", &ImageSpec::full_z)
|
||||
.def_rw("full_width", &ImageSpec::full_width)
|
||||
.def_rw("full_height", &ImageSpec::full_height)
|
||||
.def_rw("full_depth", &ImageSpec::full_depth)
|
||||
.def_rw("tile_width", &ImageSpec::tile_width)
|
||||
.def_rw("tile_height", &ImageSpec::tile_height)
|
||||
.def_rw("tile_depth", &ImageSpec::tile_depth)
|
||||
.def_rw("nchannels", &ImageSpec::nchannels)
|
||||
.def_rw("format", &ImageSpec::format)
|
||||
.def_prop_rw(
|
||||
"channelformats",
|
||||
[](const ImageSpec& spec) {
|
||||
return imagespec_get_channelformats(spec);
|
||||
},
|
||||
&imagespec_set_channelformats)
|
||||
.def_prop_rw("channelnames", &imagespec_get_channelnames,
|
||||
&imagespec_set_channelnames)
|
||||
.def_rw("alpha_channel", &ImageSpec::alpha_channel)
|
||||
.def_rw("z_channel", &ImageSpec::z_channel)
|
||||
.def_rw("deep", &ImageSpec::deep)
|
||||
.def_rw("extra_attribs", &ImageSpec::extra_attribs)
|
||||
.def_prop_rw("roi", &ImageSpec::roi, &ImageSpec::set_roi)
|
||||
.def_prop_rw("roi_full", &ImageSpec::roi_full, &ImageSpec::set_roi_full)
|
||||
.def(nb::init<>())
|
||||
.def(nb::init<int, int, int, TypeDesc>())
|
||||
.def(nb::init<const ROI&, TypeDesc>())
|
||||
.def(nb::init<TypeDesc>())
|
||||
.def(nb::init<const ImageSpec&>())
|
||||
.def("copy", [](const ImageSpec& self) { return ImageSpec(self); })
|
||||
.def("set_format",
|
||||
[](ImageSpec& self, TypeDesc t) { self.set_format(t); })
|
||||
.def("default_channel_names", &ImageSpec::default_channel_names)
|
||||
.def("channel_bytes",
|
||||
[](const ImageSpec& spec) { return spec.channel_bytes(); })
|
||||
.def(
|
||||
"channel_bytes",
|
||||
[](const ImageSpec& spec, int chan, bool native) {
|
||||
return spec.channel_bytes(chan, native);
|
||||
},
|
||||
"channel"_a, "native"_a = false)
|
||||
.def(
|
||||
"pixel_bytes",
|
||||
[](const ImageSpec& spec, bool native) {
|
||||
return spec.pixel_bytes(native);
|
||||
},
|
||||
"native"_a = false)
|
||||
.def(
|
||||
"pixel_bytes",
|
||||
[](const ImageSpec& spec, int chbegin, int chend, bool native) {
|
||||
return spec.pixel_bytes(chbegin, chend, native);
|
||||
},
|
||||
"chbegin"_a, "chend"_a, "native"_a = false)
|
||||
.def(
|
||||
"scanline_bytes",
|
||||
[](const ImageSpec& spec, bool native) {
|
||||
return spec.scanline_bytes(native);
|
||||
},
|
||||
"native"_a = false)
|
||||
.def("scanline_bytes",
|
||||
[](const ImageSpec& spec, TypeDesc type) {
|
||||
return spec.scanline_bytes(type);
|
||||
})
|
||||
.def(
|
||||
"tile_bytes",
|
||||
[](const ImageSpec& spec, bool native) {
|
||||
return spec.tile_bytes(native);
|
||||
},
|
||||
"native"_a = false)
|
||||
.def("tile_bytes", [](const ImageSpec& spec,
|
||||
TypeDesc type) { return spec.tile_bytes(type); })
|
||||
.def(
|
||||
"image_bytes",
|
||||
[](const ImageSpec& spec, bool native) {
|
||||
return spec.image_bytes(native);
|
||||
},
|
||||
"native"_a = false)
|
||||
.def("image_bytes",
|
||||
[](const ImageSpec& spec, TypeDesc type) {
|
||||
return spec.image_bytes(type);
|
||||
})
|
||||
.def("tile_pixels", &ImageSpec::tile_pixels)
|
||||
.def("image_pixels", &ImageSpec::image_pixels)
|
||||
.def("size_t_safe", &ImageSpec::size_t_safe)
|
||||
.def("channelformat", [](const ImageSpec& spec,
|
||||
int chan) { return spec.channelformat(chan); })
|
||||
.def("channel_name",
|
||||
[](const ImageSpec& spec, int chan) {
|
||||
return std::string(spec.channel_name(chan));
|
||||
})
|
||||
.def("channelindex",
|
||||
[](const ImageSpec& spec, const std::string& name) {
|
||||
return spec.channelindex(name);
|
||||
})
|
||||
.def("get_channelformats",
|
||||
[](const ImageSpec& spec) {
|
||||
return imagespec_get_channelformats(spec, false);
|
||||
})
|
||||
.def("attribute",
|
||||
[](ImageSpec& spec, const std::string& name, nb::handle obj) {
|
||||
attribute_onearg(spec, name, obj);
|
||||
})
|
||||
.def("attribute",
|
||||
[](ImageSpec& spec, const std::string& name, TypeDesc type,
|
||||
nb::handle obj) { attribute_typed(spec, name, type, obj); })
|
||||
.def(
|
||||
"get_int_attribute",
|
||||
[](const ImageSpec& spec, const std::string& name, int def) {
|
||||
return spec.get_int_attribute(name, def);
|
||||
},
|
||||
"name"_a, "defaultval"_a = 0)
|
||||
.def(
|
||||
"get_float_attribute",
|
||||
[](const ImageSpec& spec, const std::string& name, float def) {
|
||||
return spec.get_float_attribute(name, def);
|
||||
},
|
||||
"name"_a, "defaultval"_a = 0.0f)
|
||||
.def(
|
||||
"get_string_attribute",
|
||||
[](const ImageSpec& spec, const std::string& name,
|
||||
const std::string& def) {
|
||||
return std::string(spec.get_string_attribute(name, def));
|
||||
},
|
||||
"name"_a, "defaultval"_a = "")
|
||||
.def(
|
||||
"get_bytes_attribute",
|
||||
[](const ImageSpec& spec, const std::string& name,
|
||||
const std::string& def) {
|
||||
std::string s(spec.get_string_attribute(name, def));
|
||||
return nb::bytes(s.data(), s.size());
|
||||
},
|
||||
"name"_a, "defaultval"_a = "")
|
||||
.def("getattribute", &imagespec_getattribute_typed, "name"_a,
|
||||
"type"_a = TypeUnknown)
|
||||
.def(
|
||||
"get",
|
||||
[](const ImageSpec& self, const std::string& key, nb::handle def) {
|
||||
ParamValue tmpparam;
|
||||
auto p = self.find_attribute(key, tmpparam);
|
||||
if (!p)
|
||||
return nb::borrow(def);
|
||||
return make_pyobject(p->data(), p->type(), 1, def);
|
||||
},
|
||||
"key"_a, "default"_a = nb::none())
|
||||
.def(
|
||||
"erase_attribute",
|
||||
[](ImageSpec& spec, const std::string& name, TypeDesc type,
|
||||
bool casesensitive) {
|
||||
return spec.erase_attribute(name, type, casesensitive);
|
||||
},
|
||||
"name"_a = "", "type"_a = TypeUnknown, "casesensitive"_a = false)
|
||||
.def_static(
|
||||
"metadata_val",
|
||||
[](const ParamValue& p, bool human) {
|
||||
return std::string(ImageSpec::metadata_val(p, human));
|
||||
},
|
||||
"param"_a, "human"_a = false)
|
||||
.def(
|
||||
"serialize",
|
||||
[](const ImageSpec& spec, const std::string& format,
|
||||
const std::string& verbose) {
|
||||
ImageSpec::SerialFormat fmt = ImageSpec::SerialText;
|
||||
if (Strutil::iequals(format, "xml"))
|
||||
fmt = ImageSpec::SerialXML;
|
||||
ImageSpec::SerialVerbose verb = ImageSpec::SerialDetailed;
|
||||
if (Strutil::iequals(verbose, "brief"))
|
||||
verb = ImageSpec::SerialBrief;
|
||||
else if (Strutil::iequals(verbose, "detailed"))
|
||||
verb = ImageSpec::SerialDetailed;
|
||||
else if (Strutil::iequals(verbose, "detailedhuman"))
|
||||
verb = ImageSpec::SerialDetailedHuman;
|
||||
return std::string(spec.serialize(fmt, verb));
|
||||
},
|
||||
"format"_a = "text", "verbose"_a = "detailed")
|
||||
.def("to_xml",
|
||||
[](const ImageSpec& spec) { return std::string(spec.to_xml()); })
|
||||
.def("from_xml",
|
||||
[](ImageSpec& self, const std::string& xml) {
|
||||
self.from_xml(xml.c_str());
|
||||
})
|
||||
.def(
|
||||
"valid_tile_range",
|
||||
[](ImageSpec& self, int xbegin, int xend, int ybegin, int yend,
|
||||
int zbegin, int zend) {
|
||||
return self.valid_tile_range(xbegin, xend, ybegin, yend, zbegin,
|
||||
zend);
|
||||
},
|
||||
"xbegin"_a, "xend"_a, "ybegin"_a, "yend"_a, "zbegin"_a = 0,
|
||||
"zend"_a = 1)
|
||||
.def("copy_dimensions", &ImageSpec::copy_dimensions, "other"_a)
|
||||
.def(
|
||||
"set_colorspace",
|
||||
[](ImageSpec& self, const std::string& cs) {
|
||||
self.set_colorspace(cs);
|
||||
},
|
||||
"name"_a)
|
||||
.def("__getitem__",
|
||||
[](const ImageSpec& self, const std::string& key) {
|
||||
ParamValue tmpparam;
|
||||
auto p = self.find_attribute(key, tmpparam);
|
||||
if (p == nullptr) {
|
||||
std::string message = "key '" + key + "' does not exist";
|
||||
throw nb::key_error(message.c_str());
|
||||
}
|
||||
return make_pyobject(p->data(), p->type());
|
||||
})
|
||||
.def("__setitem__",
|
||||
[](ImageSpec& self, const std::string& key, nb::handle val) {
|
||||
delegate_setitem(self, key, val);
|
||||
})
|
||||
.def("__delitem__",
|
||||
[](ImageSpec& self, const std::string& key) {
|
||||
self.erase_attribute(key);
|
||||
})
|
||||
.def("__contains__", [](const ImageSpec& self, const std::string& key) {
|
||||
return self.extra_attribs.contains(key);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace PyOpenImageIO
|
||||
@@ -1,175 +0,0 @@
|
||||
// Copyright Contributors to the OpenImageIO project.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// https://github.com/AcademySoftwareFoundation/OpenImageIO
|
||||
|
||||
#include "py_oiio.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace OIIO;
|
||||
|
||||
nb::object
|
||||
oiio_getattribute_typed(const std::string& name, TypeDesc type = TypeUnknown)
|
||||
{
|
||||
if (type == TypeUnknown)
|
||||
return nb::none();
|
||||
char* data = OIIO_ALLOCA(char, type.size());
|
||||
if (!OIIO::getattribute(name, type, data))
|
||||
return nb::none();
|
||||
return PyOpenImageIO::make_pyobject(data, type);
|
||||
}
|
||||
|
||||
|
||||
struct oiio_global_attrib_wrapper {
|
||||
bool attribute(string_view name, TypeDesc type, const void* data)
|
||||
{
|
||||
return OIIO::attribute(name, type, data);
|
||||
}
|
||||
bool attribute(string_view name, int val)
|
||||
{
|
||||
return OIIO::attribute(name, val);
|
||||
}
|
||||
bool attribute(string_view name, float val)
|
||||
{
|
||||
return OIIO::attribute(name, val);
|
||||
}
|
||||
bool attribute(string_view name, const std::string& val)
|
||||
{
|
||||
return OIIO::attribute(name, val);
|
||||
}
|
||||
};
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
namespace PyOpenImageIO {
|
||||
|
||||
TypeDesc
|
||||
typedesc_from_python_array_code(string_view code)
|
||||
{
|
||||
TypeDesc t(code);
|
||||
if (!t.is_unknown())
|
||||
return t;
|
||||
|
||||
if (code == "b" || code == "c")
|
||||
return TypeDesc::INT8;
|
||||
if (code == "B")
|
||||
return TypeDesc::UINT8;
|
||||
if (code == "h")
|
||||
return TypeDesc::INT16;
|
||||
if (code == "H")
|
||||
return TypeDesc::UINT16;
|
||||
if (code == "i")
|
||||
return TypeDesc::INT;
|
||||
if (code == "I")
|
||||
return TypeDesc::UINT;
|
||||
if (code == "l")
|
||||
return TypeDesc::INT64;
|
||||
if (code == "L")
|
||||
return TypeDesc::UINT64;
|
||||
if (code == "q")
|
||||
return TypeDesc::INT64;
|
||||
if (code == "Q")
|
||||
return TypeDesc::UINT64;
|
||||
if (code == "f")
|
||||
return TypeDesc::FLOAT;
|
||||
if (code == "d")
|
||||
return TypeDesc::DOUBLE;
|
||||
if (code == "float16" || code == "e")
|
||||
return TypeDesc::HALF;
|
||||
return TypeDesc::UNKNOWN;
|
||||
}
|
||||
|
||||
|
||||
nb::object
|
||||
make_pyobject(const void* data, TypeDesc type, int nvalues,
|
||||
nb::handle defaultvalue)
|
||||
{
|
||||
if (!data || !nvalues)
|
||||
return nb::borrow(defaultvalue);
|
||||
if (type.basetype == TypeDesc::INT32)
|
||||
return C_to_val_or_tuple(static_cast<const int*>(data), type, nvalues);
|
||||
if (type.basetype == TypeDesc::FLOAT)
|
||||
return C_to_val_or_tuple(static_cast<const float*>(data), type,
|
||||
nvalues);
|
||||
if (type.basetype == TypeDesc::STRING)
|
||||
return C_to_val_or_tuple(static_cast<const char* const*>(data), type,
|
||||
nvalues);
|
||||
if (type.basetype == TypeDesc::UINT32)
|
||||
return C_to_val_or_tuple(static_cast<const unsigned int*>(data), type,
|
||||
nvalues);
|
||||
if (type.basetype == TypeDesc::INT16)
|
||||
return C_to_val_or_tuple(static_cast<const short*>(data), type,
|
||||
nvalues);
|
||||
if (type.basetype == TypeDesc::UINT16)
|
||||
return C_to_val_or_tuple(static_cast<const unsigned short*>(data), type,
|
||||
nvalues);
|
||||
if (type.basetype == TypeDesc::INT64)
|
||||
return C_to_val_or_tuple(static_cast<const int64_t*>(data), type,
|
||||
nvalues);
|
||||
if (type.basetype == TypeDesc::UINT64)
|
||||
return C_to_val_or_tuple(static_cast<const uint64_t*>(data), type,
|
||||
nvalues);
|
||||
if (type.basetype == TypeDesc::DOUBLE)
|
||||
return C_to_val_or_tuple(static_cast<const double*>(data), type,
|
||||
nvalues);
|
||||
if (type.basetype == TypeDesc::HALF)
|
||||
return C_to_val_or_tuple(static_cast<const half*>(data), type, nvalues);
|
||||
if (type.basetype == TypeDesc::UINT8 && type.arraylen > 0) {
|
||||
int n = type.arraylen * nvalues;
|
||||
if (n <= 0)
|
||||
return nb::borrow(defaultvalue);
|
||||
auto* copy = new uint8_t[n];
|
||||
std::memcpy(copy, data, static_cast<size_t>(n));
|
||||
return make_numpy_array(copy, static_cast<size_t>(n));
|
||||
}
|
||||
if (type.basetype == TypeDesc::UINT8) {
|
||||
return C_to_val_or_tuple(static_cast<const unsigned char*>(data), type,
|
||||
nvalues);
|
||||
}
|
||||
return nb::borrow(defaultvalue);
|
||||
}
|
||||
|
||||
} // namespace PyOpenImageIO
|
||||
|
||||
|
||||
NB_MODULE(_OpenImageIO, m)
|
||||
{
|
||||
m.doc() = "OpenImageIO nanobind bindings.";
|
||||
|
||||
PyOpenImageIO::declare_typedesc(m);
|
||||
PyOpenImageIO::declare_paramvalue(m);
|
||||
PyOpenImageIO::declare_roi(m);
|
||||
PyOpenImageIO::declare_imagespec(m);
|
||||
|
||||
m.def("attribute", [](const std::string& name, nb::handle obj) {
|
||||
oiio_global_attrib_wrapper wrapper;
|
||||
PyOpenImageIO::attribute_onearg(wrapper, name, obj);
|
||||
});
|
||||
m.def("attribute",
|
||||
[](const std::string& name, TypeDesc type, nb::handle obj) {
|
||||
oiio_global_attrib_wrapper wrapper;
|
||||
PyOpenImageIO::attribute_typed(wrapper, name, type, obj);
|
||||
});
|
||||
m.def(
|
||||
"get_int_attribute",
|
||||
[](const std::string& name, int def) {
|
||||
return OIIO::get_int_attribute(name, def);
|
||||
},
|
||||
"name"_a, "defaultval"_a = 0);
|
||||
m.def(
|
||||
"get_float_attribute",
|
||||
[](const std::string& name, float def) {
|
||||
return OIIO::get_float_attribute(name, def);
|
||||
},
|
||||
"name"_a, "defaultval"_a = 0.0f);
|
||||
m.def(
|
||||
"get_string_attribute",
|
||||
[](const std::string& name, const std::string& def) {
|
||||
return std::string(OIIO::get_string_attribute(name, def));
|
||||
},
|
||||
"name"_a, "defaultval"_a = "");
|
||||
m.def("getattribute", &oiio_getattribute_typed, "name"_a,
|
||||
"type"_a = TypeUnknown);
|
||||
m.attr("__version__") = OIIO_VERSION_STRING;
|
||||
}
|
||||
@@ -1,488 +0,0 @@
|
||||
// Copyright Contributors to the OpenImageIO project.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// https://github.com/AcademySoftwareFoundation/OpenImageIO
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <OpenImageIO/imagebuf.h>
|
||||
#include <OpenImageIO/imageio.h>
|
||||
#include <OpenImageIO/oiioversion.h>
|
||||
#include <OpenImageIO/span.h>
|
||||
#include <OpenImageIO/strutil.h>
|
||||
#include <OpenImageIO/typedesc.h>
|
||||
|
||||
#include <nanobind/make_iterator.h>
|
||||
#include <nanobind/nanobind.h>
|
||||
#include <nanobind/ndarray.h>
|
||||
#include <nanobind/operators.h>
|
||||
#include <nanobind/stl/string.h>
|
||||
#include <nanobind/stl/vector.h>
|
||||
|
||||
#include <cstring>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace nb = nanobind;
|
||||
using namespace nb::literals;
|
||||
|
||||
namespace PyOpenImageIO {
|
||||
|
||||
using namespace OIIO;
|
||||
|
||||
TypeDesc
|
||||
typedesc_from_python_array_code(string_view code);
|
||||
|
||||
void
|
||||
declare_roi(nb::module_& m);
|
||||
void
|
||||
declare_imagespec(nb::module_& m);
|
||||
void
|
||||
declare_typedesc(nb::module_& m);
|
||||
void
|
||||
declare_paramvalue(nb::module_& m);
|
||||
|
||||
template<typename T> struct PyTypeForCType {};
|
||||
template<> struct PyTypeForCType<int> {
|
||||
using type = nb::int_;
|
||||
};
|
||||
template<> struct PyTypeForCType<unsigned int> {
|
||||
using type = nb::int_;
|
||||
};
|
||||
template<> struct PyTypeForCType<short> {
|
||||
using type = nb::int_;
|
||||
};
|
||||
template<> struct PyTypeForCType<unsigned short> {
|
||||
using type = nb::int_;
|
||||
};
|
||||
template<> struct PyTypeForCType<char> {
|
||||
using type = nb::int_;
|
||||
};
|
||||
template<> struct PyTypeForCType<unsigned char> {
|
||||
using type = nb::int_;
|
||||
};
|
||||
template<> struct PyTypeForCType<int64_t> {
|
||||
using type = nb::int_;
|
||||
};
|
||||
template<> struct PyTypeForCType<uint64_t> {
|
||||
using type = nb::int_;
|
||||
};
|
||||
template<> struct PyTypeForCType<float> {
|
||||
using type = nb::float_;
|
||||
};
|
||||
template<> struct PyTypeForCType<half> {
|
||||
using type = nb::float_;
|
||||
};
|
||||
template<> struct PyTypeForCType<double> {
|
||||
using type = nb::float_;
|
||||
};
|
||||
template<> struct PyTypeForCType<const char*> {
|
||||
using type = nb::str;
|
||||
};
|
||||
template<> struct PyTypeForCType<std::string> {
|
||||
using type = nb::str;
|
||||
};
|
||||
|
||||
template<typename T, typename Obj>
|
||||
inline bool
|
||||
py_indexable_pod_to_stdvector(std::vector<T>& vals, const Obj& obj)
|
||||
{
|
||||
bool ok = true;
|
||||
const size_t length = obj.size();
|
||||
vals.clear();
|
||||
vals.reserve(length);
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
nb::handle elem = obj[i];
|
||||
if constexpr (std::is_same_v<T, float>) {
|
||||
if (nb::isinstance<nb::float_>(elem)) {
|
||||
vals.emplace_back(nb::cast<float>(elem));
|
||||
} else if (nb::isinstance<nb::int_>(elem)) {
|
||||
vals.emplace_back(static_cast<float>(nb::cast<int>(elem)));
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
} else if constexpr (std::is_same_v<T, int>) {
|
||||
if (nb::isinstance<nb::int_>(elem)) {
|
||||
vals.emplace_back(nb::cast<int>(elem));
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
} else if constexpr (std::is_same_v<T, unsigned int>) {
|
||||
if (nb::isinstance<nb::int_>(elem)) {
|
||||
vals.emplace_back(nb::cast<unsigned int>(elem));
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
} else if constexpr (std::is_same_v<T, unsigned char>) {
|
||||
if (nb::isinstance<nb::int_>(elem)) {
|
||||
vals.emplace_back(nb::cast<unsigned char>(elem));
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
if (!ok)
|
||||
break;
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
template<typename Obj>
|
||||
inline bool
|
||||
py_indexable_pod_to_stdvector(std::vector<std::string>& vals, const Obj& obj)
|
||||
{
|
||||
bool ok = true;
|
||||
const size_t length = obj.size();
|
||||
vals.clear();
|
||||
vals.reserve(length);
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
nb::handle elem = obj[i];
|
||||
if (nb::isinstance<nb::str>(elem)) {
|
||||
vals.emplace_back(std::string(nb::cast<nb::str>(elem).c_str()));
|
||||
} else {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
template<typename Obj>
|
||||
inline bool
|
||||
py_indexable_pod_to_stdvector(std::vector<TypeDesc>& vals, const Obj& obj)
|
||||
{
|
||||
bool ok = true;
|
||||
const size_t length = obj.size();
|
||||
vals.clear();
|
||||
vals.reserve(length);
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
nb::handle elem = obj[i];
|
||||
if (nb::isinstance<TypeDesc>(elem)) {
|
||||
vals.emplace_back(nb::cast<TypeDesc>(elem));
|
||||
} else if (nb::isinstance<TypeDesc::BASETYPE>(elem)) {
|
||||
vals.emplace_back(TypeDesc(nb::cast<TypeDesc::BASETYPE>(elem)));
|
||||
} else if (nb::isinstance<nb::str>(elem)) {
|
||||
vals.emplace_back(TypeDesc(nb::cast<nb::str>(elem).c_str()));
|
||||
} else {
|
||||
ok = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool
|
||||
py_scalar_pod_to_stdvector(std::vector<T>& vals, const nb::handle& obj)
|
||||
{
|
||||
using pytype = typename PyTypeForCType<T>::type;
|
||||
vals.clear();
|
||||
if (nb::isinstance<pytype>(obj)) {
|
||||
vals.emplace_back(nb::cast<T>(obj));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool
|
||||
py_scalar_pod_to_stdvector(std::vector<float>& vals, const nb::handle& obj)
|
||||
{
|
||||
vals.clear();
|
||||
if (nb::isinstance<nb::float_>(obj)) {
|
||||
vals.emplace_back(nb::cast<float>(obj));
|
||||
return true;
|
||||
}
|
||||
if (nb::isinstance<nb::int_>(obj)) {
|
||||
vals.emplace_back(static_cast<float>(nb::cast<int>(obj)));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool
|
||||
py_scalar_pod_to_stdvector(std::vector<std::string>& vals,
|
||||
const nb::handle& obj)
|
||||
{
|
||||
vals.clear();
|
||||
if (nb::isinstance<nb::str>(obj)) {
|
||||
vals.emplace_back(std::string(nb::cast<nb::str>(obj).c_str()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool
|
||||
py_scalar_pod_to_stdvector(std::vector<TypeDesc>& vals, const nb::handle& obj)
|
||||
{
|
||||
vals.clear();
|
||||
if (nb::isinstance<TypeDesc>(obj)) {
|
||||
vals.emplace_back(nb::cast<TypeDesc>(obj));
|
||||
return true;
|
||||
}
|
||||
if (nb::isinstance<TypeDesc::BASETYPE>(obj)) {
|
||||
vals.emplace_back(TypeDesc(nb::cast<TypeDesc::BASETYPE>(obj)));
|
||||
return true;
|
||||
}
|
||||
if (nb::isinstance<nb::str>(obj)) {
|
||||
vals.emplace_back(TypeDesc(nb::cast<nb::str>(obj).c_str()));
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool
|
||||
py_buffer_to_stdvector(std::vector<T>& vals, const nb::handle& obj)
|
||||
{
|
||||
Py_buffer view;
|
||||
if (PyObject_GetBuffer(obj.ptr(), &view, PyBUF_FORMAT | PyBUF_C_CONTIGUOUS)
|
||||
!= 0) {
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = view.itemsize > 0 && view.len % view.itemsize == 0;
|
||||
if (!ok) {
|
||||
PyBuffer_Release(&view);
|
||||
return false;
|
||||
}
|
||||
|
||||
TypeDesc format = TypeUnknown;
|
||||
if (view.format && view.format[0])
|
||||
format = typedesc_from_python_array_code(view.format);
|
||||
|
||||
const size_t count = static_cast<size_t>(view.len) / view.itemsize;
|
||||
vals.clear();
|
||||
vals.reserve(count);
|
||||
const unsigned char* data = static_cast<const unsigned char*>(view.buf);
|
||||
|
||||
for (size_t i = 0; ok && i < count; ++i) {
|
||||
if constexpr (std::is_same_v<T, float>) {
|
||||
if (format.basetype == TypeDesc::FLOAT)
|
||||
vals.emplace_back(reinterpret_cast<const float*>(data)[i]);
|
||||
else if (format.basetype == TypeDesc::INT)
|
||||
vals.emplace_back(
|
||||
static_cast<float>(reinterpret_cast<const int*>(data)[i]));
|
||||
else
|
||||
ok = false;
|
||||
} else if constexpr (std::is_same_v<T, int>) {
|
||||
if (format.basetype == TypeDesc::INT)
|
||||
vals.emplace_back(reinterpret_cast<const int*>(data)[i]);
|
||||
else
|
||||
ok = false;
|
||||
} else if constexpr (std::is_same_v<T, unsigned int>) {
|
||||
if (format.basetype == TypeDesc::UINT)
|
||||
vals.emplace_back(
|
||||
reinterpret_cast<const unsigned int*>(data)[i]);
|
||||
else
|
||||
ok = false;
|
||||
} else if constexpr (std::is_same_v<T, unsigned char>) {
|
||||
if (format.basetype == TypeDesc::UINT8)
|
||||
vals.emplace_back(
|
||||
reinterpret_cast<const unsigned char*>(data)[i]);
|
||||
else
|
||||
ok = false;
|
||||
} else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
|
||||
PyBuffer_Release(&view);
|
||||
return ok;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool
|
||||
py_buffer_to_stdvector(std::vector<std::string>&, const nb::handle&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<>
|
||||
inline bool
|
||||
py_buffer_to_stdvector(std::vector<TypeDesc>&, const nb::handle&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline bool
|
||||
py_to_stdvector(std::vector<T>& vals, const nb::handle& obj)
|
||||
{
|
||||
if (PyTuple_Check(obj.ptr()))
|
||||
return py_indexable_pod_to_stdvector(vals, nb::borrow<nb::tuple>(obj));
|
||||
if (PyList_Check(obj.ptr()))
|
||||
return py_indexable_pod_to_stdvector(vals, nb::borrow<nb::list>(obj));
|
||||
if (PyObject_CheckBuffer(obj.ptr()) && !PyUnicode_Check(obj.ptr()))
|
||||
return py_buffer_to_stdvector(vals, obj);
|
||||
return py_scalar_pod_to_stdvector(vals, obj);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline nb::tuple
|
||||
C_to_tuple(cspan<T> vals)
|
||||
{
|
||||
nb::list list;
|
||||
for (size_t i = 0; i < vals.size(); ++i)
|
||||
list.append(nb::cast(vals[i]));
|
||||
return nb::steal<nb::tuple>(PyList_AsTuple(list.ptr()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline nb::tuple
|
||||
C_to_tuple(const T* vals, size_t size)
|
||||
{
|
||||
nb::list list;
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
list.append(nb::cast(vals[i]));
|
||||
return nb::steal<nb::tuple>(PyList_AsTuple(list.ptr()));
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline nb::object
|
||||
C_to_val_or_tuple(const T* vals, TypeDesc type, int nvalues = 1)
|
||||
{
|
||||
OIIO_DASSERT(vals && nvalues);
|
||||
const size_t n = type.numelements() * type.aggregate * nvalues;
|
||||
if (n == 1 && !type.arraylen)
|
||||
return nb::cast(vals[0]);
|
||||
return C_to_tuple(vals, n);
|
||||
}
|
||||
|
||||
template<>
|
||||
inline nb::tuple
|
||||
C_to_tuple(cspan<half> vals)
|
||||
{
|
||||
nb::list list;
|
||||
for (size_t i = 0; i < vals.size(); ++i)
|
||||
list.append(static_cast<float>(vals[i]));
|
||||
return nb::steal<nb::tuple>(PyList_AsTuple(list.ptr()));
|
||||
}
|
||||
|
||||
template<>
|
||||
inline nb::tuple
|
||||
C_to_tuple(const half* vals, size_t size)
|
||||
{
|
||||
nb::list list;
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
list.append(static_cast<float>(vals[i]));
|
||||
return nb::steal<nb::tuple>(PyList_AsTuple(list.ptr()));
|
||||
}
|
||||
|
||||
template<>
|
||||
inline nb::object
|
||||
C_to_val_or_tuple(const half* vals, TypeDesc type, int nvalues)
|
||||
{
|
||||
OIIO_DASSERT(vals && nvalues);
|
||||
const size_t n = type.numelements() * type.aggregate * nvalues;
|
||||
if (n == 1 && !type.arraylen)
|
||||
return nb::cast(static_cast<float>(vals[0]));
|
||||
return C_to_tuple(vals, n);
|
||||
}
|
||||
|
||||
template<typename T, typename Obj>
|
||||
bool
|
||||
attribute_typed(T& myobj, string_view name, TypeDesc type, const Obj& dataobj)
|
||||
{
|
||||
if (type.basetype == TypeDesc::INT) {
|
||||
std::vector<int> vals;
|
||||
bool ok = py_to_stdvector(vals, dataobj);
|
||||
ok &= (vals.size() == type.numelements() * type.aggregate);
|
||||
if (ok)
|
||||
myobj.attribute(name, type, vals.data());
|
||||
return ok;
|
||||
}
|
||||
if (type.basetype == TypeDesc::UINT) {
|
||||
std::vector<unsigned int> vals;
|
||||
bool ok = py_to_stdvector(vals, dataobj);
|
||||
ok &= (vals.size() == type.numelements() * type.aggregate);
|
||||
if (ok)
|
||||
myobj.attribute(name, type, vals.data());
|
||||
return ok;
|
||||
}
|
||||
if (type.basetype == TypeDesc::UINT8) {
|
||||
std::vector<unsigned char> vals;
|
||||
bool ok = py_to_stdvector(vals, dataobj);
|
||||
ok &= (vals.size() == type.numelements() * type.aggregate);
|
||||
if (ok)
|
||||
myobj.attribute(name, type, vals.data());
|
||||
return ok;
|
||||
}
|
||||
if (type.basetype == TypeDesc::FLOAT) {
|
||||
std::vector<float> vals;
|
||||
bool ok = py_to_stdvector(vals, dataobj);
|
||||
ok &= (vals.size() == type.numelements() * type.aggregate);
|
||||
if (ok)
|
||||
myobj.attribute(name, type, vals.data());
|
||||
return ok;
|
||||
}
|
||||
if (type.basetype == TypeDesc::STRING) {
|
||||
std::vector<std::string> vals;
|
||||
bool ok = py_to_stdvector(vals, dataobj);
|
||||
ok &= (vals.size() == type.numelements() * type.aggregate);
|
||||
if (ok) {
|
||||
std::vector<ustring> u;
|
||||
u.reserve(vals.size());
|
||||
for (auto& val : vals)
|
||||
u.emplace_back(val);
|
||||
myobj.attribute(name, type, u.data());
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
inline void
|
||||
attribute_onearg(T& myobj, string_view name, const nb::handle& obj)
|
||||
{
|
||||
if (nb::isinstance<nb::float_>(obj))
|
||||
myobj.attribute(name, nb::cast<float>(obj));
|
||||
else if (nb::isinstance<nb::int_>(obj))
|
||||
myobj.attribute(name, nb::cast<int>(obj));
|
||||
else if (nb::isinstance<nb::str>(obj))
|
||||
myobj.attribute(name, std::string(nb::cast<nb::str>(obj).c_str()));
|
||||
else if (nb::isinstance<nb::bytes>(obj)) {
|
||||
nb::bytes bytes = nb::cast<nb::bytes>(obj);
|
||||
myobj.attribute(name, std::string(bytes.c_str(), bytes.size()));
|
||||
} else
|
||||
throw nb::type_error("attribute() value must be int, float, or str");
|
||||
}
|
||||
|
||||
template<class T>
|
||||
inline nb::object
|
||||
make_numpy_array(T* data, size_t size)
|
||||
{
|
||||
nb::capsule owner(data, [](void* p) noexcept {
|
||||
delete[] reinterpret_cast<T*>(p);
|
||||
});
|
||||
nb::ndarray<nb::numpy, T, nb::shape<-1>> array(data, { size }, owner);
|
||||
return nb::cast(std::move(array), nb::rv_policy::move);
|
||||
}
|
||||
|
||||
nb::object
|
||||
make_pyobject(const void* data, TypeDesc type, int nvalues = 1,
|
||||
nb::handle defaultvalue = nb::none());
|
||||
|
||||
template<typename C>
|
||||
inline void
|
||||
delegate_setitem(C& self, const std::string& key, const nb::handle& obj)
|
||||
{
|
||||
if (nb::isinstance<nb::float_>(obj))
|
||||
self[key] = nb::cast<float>(obj);
|
||||
else if (nb::isinstance<nb::int_>(obj))
|
||||
self[key] = nb::cast<int>(obj);
|
||||
else if (nb::isinstance<nb::str>(obj))
|
||||
self[key] = std::string(nb::cast<nb::str>(obj).c_str());
|
||||
else if (nb::isinstance<nb::bytes>(obj)) {
|
||||
nb::bytes bytes = nb::cast<nb::bytes>(obj);
|
||||
self[key] = std::string(bytes.c_str(), bytes.size());
|
||||
} else
|
||||
throw std::invalid_argument("Bad type for __setitem__");
|
||||
}
|
||||
|
||||
} // namespace PyOpenImageIO
|
||||
@@ -1,248 +0,0 @@
|
||||
// Copyright Contributors to the OpenImageIO project.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// https://github.com/AcademySoftwareFoundation/OpenImageIO
|
||||
|
||||
#include "py_oiio.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace OIIO;
|
||||
|
||||
template<typename T, typename Obj>
|
||||
bool
|
||||
attribute_typed_nvalues(T& myobj, string_view name, TypeDesc type, int nvalues,
|
||||
const Obj& dataobj)
|
||||
{
|
||||
if (type.basetype == TypeDesc::INT) {
|
||||
std::vector<int> vals;
|
||||
bool ok = PyOpenImageIO::py_to_stdvector(vals, dataobj);
|
||||
ok &= (vals.size() == type.numelements() * type.aggregate * nvalues);
|
||||
if (ok)
|
||||
myobj.attribute(name, type, nvalues, vals.data());
|
||||
return ok;
|
||||
}
|
||||
if (type.basetype == TypeDesc::UINT) {
|
||||
std::vector<unsigned int> vals;
|
||||
bool ok = PyOpenImageIO::py_to_stdvector(vals, dataobj);
|
||||
ok &= (vals.size() == type.numelements() * type.aggregate * nvalues);
|
||||
if (ok)
|
||||
myobj.attribute(name, type, nvalues, vals.data());
|
||||
return ok;
|
||||
}
|
||||
if (type.basetype == TypeDesc::FLOAT) {
|
||||
std::vector<float> vals;
|
||||
bool ok = PyOpenImageIO::py_to_stdvector(vals, dataobj);
|
||||
ok &= (vals.size() == type.numelements() * type.aggregate * nvalues);
|
||||
if (ok)
|
||||
myobj.attribute(name, type, nvalues, vals.data());
|
||||
return ok;
|
||||
}
|
||||
if (type.basetype == TypeDesc::STRING) {
|
||||
std::vector<std::string> vals;
|
||||
bool ok = PyOpenImageIO::py_to_stdvector(vals, dataobj);
|
||||
ok &= (vals.size() == type.numelements() * type.aggregate * nvalues);
|
||||
if (ok) {
|
||||
std::vector<ustring> converted;
|
||||
converted.reserve(vals.size());
|
||||
for (auto& val : vals)
|
||||
converted.emplace_back(val);
|
||||
myobj.attribute(name, type, nvalues, converted.data());
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
if (type.basetype == TypeDesc::UINT8) {
|
||||
std::vector<uint8_t> vals;
|
||||
bool ok = PyOpenImageIO::py_to_stdvector(vals, dataobj);
|
||||
ok &= (vals.size() == type.numelements() * type.aggregate * nvalues);
|
||||
if (ok)
|
||||
myobj.attribute(name, type, nvalues, vals.data());
|
||||
return ok;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
ParamValue
|
||||
paramvalue_from_pyobject(string_view name, TypeDesc type, int nvalues,
|
||||
ParamValue::Interp interp, nb::handle obj)
|
||||
{
|
||||
ParamValue pv;
|
||||
// Unsized uint8[] + bytes infers arraylen — must run before numelements().
|
||||
if (type.basetype == TypeDesc::UINT8 && type.arraylen
|
||||
&& nb::isinstance<nb::bytes>(obj)) {
|
||||
TypeDesc t = type;
|
||||
nb::bytes b = nb::cast<nb::bytes>(obj);
|
||||
const std::string s(b.c_str(), b.size());
|
||||
if (t.arraylen < 0)
|
||||
t.arraylen = static_cast<int>(s.size()) / nvalues;
|
||||
if (t.arraylen * nvalues == static_cast<int>(s.size())) {
|
||||
std::vector<uint8_t> vals(reinterpret_cast<const uint8_t*>(s.data()),
|
||||
reinterpret_cast<const uint8_t*>(s.data())
|
||||
+ s.size());
|
||||
pv.init(name, t, nvalues, interp, vals.data());
|
||||
}
|
||||
return pv;
|
||||
}
|
||||
|
||||
const size_t expected_size = static_cast<size_t>(
|
||||
type.numelements() * type.aggregate * nvalues);
|
||||
if (type.basetype == TypeDesc::INT) {
|
||||
std::vector<int> vals;
|
||||
if (PyOpenImageIO::py_to_stdvector(vals, obj)
|
||||
&& vals.size() >= expected_size) {
|
||||
pv.init(name, type, nvalues, interp, vals.data());
|
||||
}
|
||||
} else if (type.basetype == TypeDesc::UINT) {
|
||||
std::vector<unsigned int> vals;
|
||||
if (PyOpenImageIO::py_to_stdvector(vals, obj)
|
||||
&& vals.size() >= expected_size) {
|
||||
pv.init(name, type, nvalues, interp, vals.data());
|
||||
}
|
||||
} else if (type.basetype == TypeDesc::FLOAT) {
|
||||
std::vector<float> vals;
|
||||
if (PyOpenImageIO::py_to_stdvector(vals, obj)
|
||||
&& vals.size() >= expected_size) {
|
||||
pv.init(name, type, nvalues, interp, vals.data());
|
||||
}
|
||||
} else if (type.basetype == TypeDesc::STRING) {
|
||||
std::vector<std::string> vals;
|
||||
if (PyOpenImageIO::py_to_stdvector(vals, obj)
|
||||
&& vals.size() >= expected_size) {
|
||||
std::vector<ustring> converted;
|
||||
converted.reserve(vals.size());
|
||||
for (auto& val : vals)
|
||||
converted.emplace_back(val);
|
||||
pv.init(name, type, nvalues, interp, converted.data());
|
||||
}
|
||||
} else if (type.basetype == TypeDesc::UINT8) {
|
||||
std::vector<uint8_t> vals;
|
||||
if (PyOpenImageIO::py_to_stdvector(vals, obj)
|
||||
&& vals.size() >= expected_size) {
|
||||
pv.init(name, type, nvalues, interp, vals.data());
|
||||
}
|
||||
}
|
||||
return pv;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
namespace PyOpenImageIO {
|
||||
|
||||
void
|
||||
declare_paramvalue(nb::module_& m)
|
||||
{
|
||||
nb::enum_<ParamValue::Interp>(m, "Interp")
|
||||
.value("CONSTANT", ParamValue::INTERP_CONSTANT)
|
||||
.value("PERPIECE", ParamValue::INTERP_PERPIECE)
|
||||
.value("LINEAR", ParamValue::INTERP_LINEAR)
|
||||
.value("VERTEX", ParamValue::INTERP_VERTEX)
|
||||
.value("INTERP_CONSTANT", ParamValue::INTERP_CONSTANT)
|
||||
.value("INTERP_PERPIECE", ParamValue::INTERP_PERPIECE)
|
||||
.value("INTERP_LINEAR", ParamValue::INTERP_LINEAR)
|
||||
.value("INTERP_VERTEX", ParamValue::INTERP_VERTEX);
|
||||
|
||||
nb::class_<ParamValue>(m, "ParamValue")
|
||||
.def_prop_ro("name",
|
||||
[](const ParamValue& self) {
|
||||
return std::string(self.name().string());
|
||||
})
|
||||
.def_prop_ro("type", [](const ParamValue& self) { return self.type(); })
|
||||
.def_prop_ro("value",
|
||||
[](const ParamValue& self) {
|
||||
return make_pyobject(self.data(), self.type(),
|
||||
self.nvalues());
|
||||
})
|
||||
.def("__len__", &ParamValue::nvalues)
|
||||
.def(nb::init<const std::string&, int>())
|
||||
.def(nb::init<const std::string&, float>())
|
||||
.def(nb::init<const std::string&, const std::string&>())
|
||||
.def("__init__",
|
||||
[](ParamValue* self, const std::string& name, TypeDesc type,
|
||||
nb::handle obj) {
|
||||
new (self) ParamValue(paramvalue_from_pyobject(
|
||||
name, type, 1, ParamValue::INTERP_CONSTANT, obj));
|
||||
})
|
||||
.def("__init__", [](ParamValue* self, const std::string& name,
|
||||
TypeDesc type, int nvalues,
|
||||
ParamValue::Interp interp, nb::handle obj) {
|
||||
new (self) ParamValue(
|
||||
paramvalue_from_pyobject(name, type, nvalues, interp, obj));
|
||||
});
|
||||
|
||||
nb::class_<ParamValueList>(m, "ParamValueList")
|
||||
.def(nb::init<>())
|
||||
.def(
|
||||
"__getitem__",
|
||||
[](const ParamValueList& self, size_t i) {
|
||||
if (i >= self.size())
|
||||
throw nb::index_error();
|
||||
return self[i];
|
||||
},
|
||||
nb::rv_policy::reference_internal)
|
||||
.def("__getitem__",
|
||||
[](const ParamValueList& self, const std::string& key) {
|
||||
auto p = self.find(key);
|
||||
if (p == self.end()) {
|
||||
std::string message = "key '" + key + "' does not exist";
|
||||
throw nb::key_error(message.c_str());
|
||||
}
|
||||
return make_pyobject(p->data(), p->type());
|
||||
})
|
||||
.def("__setitem__",
|
||||
[](ParamValueList& self, const std::string& key, nb::handle val) {
|
||||
delegate_setitem(self, key, val);
|
||||
})
|
||||
.def("__delitem__", [](ParamValueList& self,
|
||||
const std::string& key) { self.remove(key); })
|
||||
.def("__contains__",
|
||||
[](const ParamValueList& self, const std::string& key) {
|
||||
return self.contains(key);
|
||||
})
|
||||
.def("__len__", [](const ParamValueList& self) { return self.size(); })
|
||||
.def(
|
||||
"__iter__",
|
||||
[](const ParamValueList& self) {
|
||||
return nb::make_iterator(nb::type<ParamValueList>(), "iterator",
|
||||
self.begin(), self.end());
|
||||
},
|
||||
nb::keep_alive<0, 1>())
|
||||
.def("append", [](ParamValueList& self,
|
||||
const ParamValue& value) { self.push_back(value); })
|
||||
.def("clear", &ParamValueList::clear)
|
||||
.def("free", &ParamValueList::free)
|
||||
.def("resize",
|
||||
[](ParamValueList& self, size_t size) { self.resize(size); })
|
||||
.def(
|
||||
"remove",
|
||||
[](ParamValueList& self, const std::string& name, TypeDesc type,
|
||||
bool casesensitive) { self.remove(name, type, casesensitive); },
|
||||
"name"_a, "type"_a = TypeUnknown, "casesensitive"_a = true)
|
||||
.def(
|
||||
"contains",
|
||||
[](const ParamValueList& self, const std::string& name,
|
||||
TypeDesc type, bool casesensitive) {
|
||||
return self.contains(name, type, casesensitive);
|
||||
},
|
||||
"name"_a, "type"_a = TypeUnknown, "casesensitive"_a = true)
|
||||
.def(
|
||||
"add_or_replace",
|
||||
[](ParamValueList& self, const ParamValue& pv, bool casesensitive) {
|
||||
return self.add_or_replace(pv, casesensitive);
|
||||
},
|
||||
"value"_a, "casesensitive"_a = true)
|
||||
.def("sort", &ParamValueList::sort, "casesensitive"_a = true)
|
||||
.def("merge", &ParamValueList::merge, "other"_a, "override"_a = false)
|
||||
.def("attribute",
|
||||
[](ParamValueList& self, const std::string& name, nb::handle val) {
|
||||
attribute_onearg(self, name, val);
|
||||
})
|
||||
.def("attribute",
|
||||
[](ParamValueList& self, const std::string& name, TypeDesc type,
|
||||
nb::handle obj) { attribute_typed(self, name, type, obj); })
|
||||
.def("attribute", [](ParamValueList& self, const std::string& name,
|
||||
TypeDesc type, int nvalues, nb::handle obj) {
|
||||
attribute_typed_nvalues(self, name, type, nvalues, obj);
|
||||
});
|
||||
}
|
||||
|
||||
} // namespace PyOpenImageIO
|
||||
@@ -1,70 +0,0 @@
|
||||
// Copyright Contributors to the OpenImageIO project.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// https://github.com/AcademySoftwareFoundation/OpenImageIO
|
||||
|
||||
#include "py_oiio.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace OIIO;
|
||||
|
||||
bool
|
||||
roi_contains_coord(const ROI& roi, int x, int y, int z, int ch)
|
||||
{
|
||||
return roi.contains(x, y, z, ch);
|
||||
}
|
||||
|
||||
|
||||
bool
|
||||
roi_contains_roi(const ROI& roi, const ROI& other)
|
||||
{
|
||||
return roi.contains(other);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
namespace PyOpenImageIO {
|
||||
|
||||
void
|
||||
declare_roi(nb::module_& m)
|
||||
{
|
||||
nb::class_<ROI> roi(m, "ROI");
|
||||
roi.def_rw("xbegin", &ROI::xbegin)
|
||||
.def_rw("xend", &ROI::xend)
|
||||
.def_rw("ybegin", &ROI::ybegin)
|
||||
.def_rw("yend", &ROI::yend)
|
||||
.def_rw("zbegin", &ROI::zbegin)
|
||||
.def_rw("zend", &ROI::zend)
|
||||
.def_rw("chbegin", &ROI::chbegin)
|
||||
.def_rw("chend", &ROI::chend)
|
||||
.def(nb::init<>())
|
||||
.def(nb::init<int, int, int, int>())
|
||||
.def(nb::init<int, int, int, int, int, int>())
|
||||
.def(nb::init<int, int, int, int, int, int, int, int>())
|
||||
.def(nb::init<const ROI&>())
|
||||
.def_prop_ro("defined", &ROI::defined)
|
||||
.def_prop_ro("width", &ROI::width)
|
||||
.def_prop_ro("height", &ROI::height)
|
||||
.def_prop_ro("depth", &ROI::depth)
|
||||
.def_prop_ro("nchannels", &ROI::nchannels)
|
||||
.def_prop_ro("npixels", &ROI::npixels)
|
||||
.def("contains", &roi_contains_coord, "x"_a, "y"_a, "z"_a = 0,
|
||||
"ch"_a = 0)
|
||||
.def("contains", &roi_contains_roi, "other"_a)
|
||||
.def_prop_ro_static("All", [](nb::handle) { return ROI::All(); })
|
||||
.def("__str__",
|
||||
[](const ROI& roi_) { return Strutil::fmt::format("{}", roi_); })
|
||||
.def("copy", [](const ROI& self) { return self; })
|
||||
.def(nb::self == nb::self)
|
||||
.def(nb::self != nb::self);
|
||||
|
||||
m.def("union", &roi_union);
|
||||
m.def("intersection", &roi_intersection);
|
||||
m.def("get_roi", &get_roi);
|
||||
m.def("get_roi_full", &get_roi_full);
|
||||
m.def("set_roi", &set_roi);
|
||||
m.def("set_roi_full", &set_roi_full);
|
||||
}
|
||||
|
||||
} // namespace PyOpenImageIO
|
||||
@@ -1,246 +0,0 @@
|
||||
// Copyright Contributors to the OpenImageIO project.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// https://github.com/AcademySoftwareFoundation/OpenImageIO
|
||||
|
||||
#include "py_oiio.h"
|
||||
|
||||
namespace {
|
||||
|
||||
using namespace OIIO;
|
||||
|
||||
template<class Enum>
|
||||
void
|
||||
typedesc_property(TypeDesc& t, Enum value);
|
||||
|
||||
template<>
|
||||
void
|
||||
typedesc_property<TypeDesc::BASETYPE>(TypeDesc& t, TypeDesc::BASETYPE value)
|
||||
{
|
||||
t.basetype = value;
|
||||
}
|
||||
|
||||
template<>
|
||||
void
|
||||
typedesc_property<TypeDesc::AGGREGATE>(TypeDesc& t, TypeDesc::AGGREGATE value)
|
||||
{
|
||||
t.aggregate = value;
|
||||
}
|
||||
|
||||
template<>
|
||||
void
|
||||
typedesc_property<TypeDesc::VECSEMANTICS>(TypeDesc& t,
|
||||
TypeDesc::VECSEMANTICS value)
|
||||
{
|
||||
t.vecsemantics = value;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
|
||||
namespace PyOpenImageIO {
|
||||
|
||||
void
|
||||
declare_typedesc(nb::module_& m)
|
||||
{
|
||||
using BASETYPE = TypeDesc::BASETYPE;
|
||||
using AGGREGATE = TypeDesc::AGGREGATE;
|
||||
using VECSEMANTICS = TypeDesc::VECSEMANTICS;
|
||||
|
||||
nb::enum_<BASETYPE>(m, "BASETYPE")
|
||||
.value("UNKNOWN", TypeDesc::UNKNOWN)
|
||||
.value("NONE", TypeDesc::NONE)
|
||||
.value("UCHAR", TypeDesc::UCHAR)
|
||||
.value("UINT8", TypeDesc::UINT8)
|
||||
.value("CHAR", TypeDesc::CHAR)
|
||||
.value("INT8", TypeDesc::INT8)
|
||||
.value("UINT16", TypeDesc::UINT16)
|
||||
.value("USHORT", TypeDesc::USHORT)
|
||||
.value("SHORT", TypeDesc::SHORT)
|
||||
.value("INT16", TypeDesc::INT16)
|
||||
.value("UINT", TypeDesc::UINT)
|
||||
.value("UINT32", TypeDesc::UINT32)
|
||||
.value("INT", TypeDesc::INT)
|
||||
.value("INT32", TypeDesc::INT32)
|
||||
.value("ULONGLONG", TypeDesc::ULONGLONG)
|
||||
.value("UINT64", TypeDesc::UINT64)
|
||||
.value("LONGLONG", TypeDesc::LONGLONG)
|
||||
.value("INT64", TypeDesc::INT64)
|
||||
.value("HALF", TypeDesc::HALF)
|
||||
.value("FLOAT", TypeDesc::FLOAT)
|
||||
.value("DOUBLE", TypeDesc::DOUBLE)
|
||||
.value("STRING", TypeDesc::STRING)
|
||||
.value("PTR", TypeDesc::PTR)
|
||||
.value("LASTBASE", TypeDesc::LASTBASE)
|
||||
.export_values();
|
||||
|
||||
nb::enum_<AGGREGATE>(m, "AGGREGATE")
|
||||
.value("SCALAR", TypeDesc::SCALAR)
|
||||
.value("VEC2", TypeDesc::VEC2)
|
||||
.value("VEC3", TypeDesc::VEC3)
|
||||
.value("VEC4", TypeDesc::VEC4)
|
||||
.value("MATRIX33", TypeDesc::MATRIX33)
|
||||
.value("MATRIX44", TypeDesc::MATRIX44)
|
||||
.export_values();
|
||||
|
||||
nb::enum_<VECSEMANTICS>(m, "VECSEMANTICS")
|
||||
.value("NOXFORM", TypeDesc::NOXFORM)
|
||||
.value("NOSEMANTICS", TypeDesc::NOSEMANTICS)
|
||||
.value("COLOR", TypeDesc::COLOR)
|
||||
.value("POINT", TypeDesc::POINT)
|
||||
.value("VECTOR", TypeDesc::VECTOR)
|
||||
.value("NORMAL", TypeDesc::NORMAL)
|
||||
.value("TIMECODE", TypeDesc::TIMECODE)
|
||||
.value("KEYCODE", TypeDesc::KEYCODE)
|
||||
.value("RATIONAL", TypeDesc::RATIONAL)
|
||||
.value("BOX", TypeDesc::BOX)
|
||||
.export_values();
|
||||
|
||||
nb::class_<TypeDesc>(m, "TypeDesc")
|
||||
.def_prop_rw(
|
||||
"basetype", [](TypeDesc t) { return BASETYPE(t.basetype); },
|
||||
[](TypeDesc& t, BASETYPE b) { typedesc_property(t, b); })
|
||||
.def_prop_rw(
|
||||
"aggregate", [](TypeDesc t) { return AGGREGATE(t.aggregate); },
|
||||
[](TypeDesc& t, AGGREGATE b) { typedesc_property(t, b); })
|
||||
.def_prop_rw(
|
||||
"vecsemantics",
|
||||
[](TypeDesc t) { return VECSEMANTICS(t.vecsemantics); },
|
||||
[](TypeDesc& t, VECSEMANTICS b) { typedesc_property(t, b); })
|
||||
.def_rw("arraylen", &TypeDesc::arraylen)
|
||||
.def(nb::init<>())
|
||||
.def(nb::init<const TypeDesc&>())
|
||||
.def(nb::init<BASETYPE>())
|
||||
.def(nb::init<BASETYPE, AGGREGATE>())
|
||||
.def(nb::init<BASETYPE, AGGREGATE, VECSEMANTICS>())
|
||||
.def(nb::init<BASETYPE, AGGREGATE, VECSEMANTICS, int>())
|
||||
.def(nb::init<const char*>())
|
||||
.def("c_str",
|
||||
[](const TypeDesc& self) { return std::string(self.c_str()); })
|
||||
.def("numelements", &TypeDesc::numelements)
|
||||
.def("basevalues", &TypeDesc::basevalues)
|
||||
.def("size", &TypeDesc::size)
|
||||
.def("elementtype", &TypeDesc::elementtype)
|
||||
.def("elementsize", &TypeDesc::elementsize)
|
||||
.def("basesize", &TypeDesc::basesize)
|
||||
.def("fromstring",
|
||||
[](TypeDesc& t, const char* typestring) {
|
||||
t.fromstring(typestring);
|
||||
})
|
||||
.def("equivalent", &TypeDesc::equivalent)
|
||||
.def("unarray", &TypeDesc::unarray)
|
||||
.def("is_vec2",
|
||||
[](const TypeDesc& t, BASETYPE b = TypeDesc::FLOAT) {
|
||||
return t.is_vec2(b);
|
||||
})
|
||||
.def("is_vec3",
|
||||
[](const TypeDesc& t, BASETYPE b = TypeDesc::FLOAT) {
|
||||
return t.is_vec3(b);
|
||||
})
|
||||
.def("is_vec4",
|
||||
[](const TypeDesc& t, BASETYPE b = TypeDesc::FLOAT) {
|
||||
return t.is_vec4(b);
|
||||
})
|
||||
.def("is_box2",
|
||||
[](const TypeDesc& t, BASETYPE b = TypeDesc::FLOAT) {
|
||||
return t.is_box2(b);
|
||||
})
|
||||
.def("is_box3",
|
||||
[](const TypeDesc& t, BASETYPE b = TypeDesc::FLOAT) {
|
||||
return t.is_box3(b);
|
||||
})
|
||||
.def_static("all_types_equal",
|
||||
[](const std::vector<TypeDesc>& types) {
|
||||
return TypeDesc::all_types_equal(types);
|
||||
})
|
||||
.def(nb::self == nb::self)
|
||||
.def(nb::self != nb::self)
|
||||
.def("__str__", [](TypeDesc t) { return std::string(t.c_str()); })
|
||||
.def("__repr__", [](TypeDesc t) {
|
||||
return Strutil::fmt::format("<TypeDesc '{}'>", t.c_str());
|
||||
});
|
||||
|
||||
nb::implicitly_convertible<BASETYPE, TypeDesc>();
|
||||
nb::implicitly_convertible<nb::str, TypeDesc>();
|
||||
|
||||
m.attr("UNKNOWN") = nb::cast(TypeDesc::UNKNOWN);
|
||||
m.attr("NONE") = nb::cast(TypeDesc::NONE);
|
||||
m.attr("UCHAR") = nb::cast(TypeDesc::UCHAR);
|
||||
m.attr("UINT8") = nb::cast(TypeDesc::UINT8);
|
||||
m.attr("CHAR") = nb::cast(TypeDesc::CHAR);
|
||||
m.attr("INT8") = nb::cast(TypeDesc::INT8);
|
||||
m.attr("UINT16") = nb::cast(TypeDesc::UINT16);
|
||||
m.attr("USHORT") = nb::cast(TypeDesc::USHORT);
|
||||
m.attr("SHORT") = nb::cast(TypeDesc::SHORT);
|
||||
m.attr("INT16") = nb::cast(TypeDesc::INT16);
|
||||
m.attr("UINT") = nb::cast(TypeDesc::UINT);
|
||||
m.attr("UINT32") = nb::cast(TypeDesc::UINT32);
|
||||
m.attr("INT") = nb::cast(TypeDesc::INT);
|
||||
m.attr("INT32") = nb::cast(TypeDesc::INT32);
|
||||
m.attr("ULONGLONG") = nb::cast(TypeDesc::ULONGLONG);
|
||||
m.attr("UINT64") = nb::cast(TypeDesc::UINT64);
|
||||
m.attr("LONGLONG") = nb::cast(TypeDesc::LONGLONG);
|
||||
m.attr("INT64") = nb::cast(TypeDesc::INT64);
|
||||
m.attr("HALF") = nb::cast(TypeDesc::HALF);
|
||||
m.attr("FLOAT") = nb::cast(TypeDesc::FLOAT);
|
||||
m.attr("DOUBLE") = nb::cast(TypeDesc::DOUBLE);
|
||||
m.attr("STRING") = nb::cast(TypeDesc::STRING);
|
||||
m.attr("PTR") = nb::cast(TypeDesc::PTR);
|
||||
m.attr("LASTBASE") = nb::cast(TypeDesc::LASTBASE);
|
||||
|
||||
m.attr("SCALAR") = nb::cast(TypeDesc::SCALAR);
|
||||
m.attr("VEC2") = nb::cast(TypeDesc::VEC2);
|
||||
m.attr("VEC3") = nb::cast(TypeDesc::VEC3);
|
||||
m.attr("VEC4") = nb::cast(TypeDesc::VEC4);
|
||||
m.attr("MATRIX33") = nb::cast(TypeDesc::MATRIX33);
|
||||
m.attr("MATRIX44") = nb::cast(TypeDesc::MATRIX44);
|
||||
|
||||
m.attr("NOXFORM") = nb::cast(TypeDesc::NOXFORM);
|
||||
m.attr("NOSEMANTICS") = nb::cast(TypeDesc::NOSEMANTICS);
|
||||
m.attr("COLOR") = nb::cast(TypeDesc::COLOR);
|
||||
m.attr("POINT") = nb::cast(TypeDesc::POINT);
|
||||
m.attr("VECTOR") = nb::cast(TypeDesc::VECTOR);
|
||||
m.attr("NORMAL") = nb::cast(TypeDesc::NORMAL);
|
||||
m.attr("TIMECODE") = nb::cast(TypeDesc::TIMECODE);
|
||||
m.attr("KEYCODE") = nb::cast(TypeDesc::KEYCODE);
|
||||
m.attr("RATIONAL") = nb::cast(TypeDesc::RATIONAL);
|
||||
m.attr("BOX") = nb::cast(TypeDesc::BOX);
|
||||
|
||||
m.attr("TypeUnknown") = TypeUnknown;
|
||||
m.attr("TypeFloat") = TypeFloat;
|
||||
m.attr("TypeColor") = TypeColor;
|
||||
m.attr("TypePoint") = TypePoint;
|
||||
m.attr("TypeVector") = TypeVector;
|
||||
m.attr("TypeNormal") = TypeNormal;
|
||||
m.attr("TypeString") = TypeString;
|
||||
m.attr("TypeInt") = TypeInt;
|
||||
m.attr("TypeUInt") = TypeUInt;
|
||||
m.attr("TypeInt64") = TypeInt64;
|
||||
m.attr("TypeUInt64") = TypeUInt64;
|
||||
m.attr("TypeInt32") = TypeInt32;
|
||||
m.attr("TypeUInt32") = TypeUInt32;
|
||||
m.attr("TypeInt16") = TypeInt16;
|
||||
m.attr("TypeUInt16") = TypeUInt16;
|
||||
m.attr("TypeInt8") = TypeInt8;
|
||||
m.attr("TypeUInt8") = TypeUInt8;
|
||||
m.attr("TypeHalf") = TypeHalf;
|
||||
m.attr("TypeMatrix") = TypeMatrix;
|
||||
m.attr("TypeMatrix33") = TypeMatrix33;
|
||||
m.attr("TypeMatrix44") = TypeMatrix44;
|
||||
m.attr("TypeTimeCode") = TypeTimeCode;
|
||||
m.attr("TypeKeyCode") = TypeKeyCode;
|
||||
m.attr("TypeFloat2") = TypeFloat2;
|
||||
m.attr("TypeVector2") = TypeVector2;
|
||||
m.attr("TypeFloat4") = TypeFloat4;
|
||||
m.attr("TypeVector4") = TypeVector4;
|
||||
m.attr("TypeVector2i") = TypeVector2i;
|
||||
m.attr("TypeVector3i") = TypeVector3i;
|
||||
m.attr("TypeBox2") = TypeBox2;
|
||||
m.attr("TypeBox3") = TypeBox3;
|
||||
m.attr("TypeBox2i") = TypeBox2i;
|
||||
m.attr("TypeBox3i") = TypeBox3i;
|
||||
m.attr("TypeRational") = TypeRational;
|
||||
m.attr("TypeURational") = TypeURational;
|
||||
m.attr("TypePointer") = TypePointer;
|
||||
}
|
||||
|
||||
} // namespace PyOpenImageIO
|
||||
+37
-14
@@ -4,19 +4,42 @@
|
||||
|
||||
add_subdirectory (stubs)
|
||||
|
||||
file (GLOB python_srcs *.cpp)
|
||||
setup_python_module (TARGET PyOpenImageIO
|
||||
MODULE OpenImageIO
|
||||
SOURCES ${python_srcs}
|
||||
LIBS OpenImageIO
|
||||
${OPENIMAGEIO_IMATH_TARGETS}
|
||||
)
|
||||
set (python_dual_backend_srcs
|
||||
py_roi.cpp
|
||||
py_typedesc.cpp
|
||||
py_imagespec.cpp
|
||||
py_paramvalue.cpp)
|
||||
|
||||
# Unity builds: If in unity group mode, make the python bindings one group. If
|
||||
# in unity batch mode, use the smaller batch size because these tend to be
|
||||
# expensive files to compile.
|
||||
set_target_properties(PyOpenImageIO PROPERTIES
|
||||
UNITY_BUILD_BATCH_SIZE ${UNITY_SMALL_BATCH_SIZE})
|
||||
set_source_files_properties(${python_srcs} PROPERTIES
|
||||
UNITY_GROUP PyOpenImageIO)
|
||||
# Full pybind11 module (pybind11 or both backends).
|
||||
if (OIIO_BUILD_PYTHON_PYBIND11)
|
||||
file (GLOB python_srcs *.cpp)
|
||||
setup_python_module (TARGET PyOpenImageIO
|
||||
MODULE OpenImageIO
|
||||
SOURCES ${python_srcs}
|
||||
LIBS OpenImageIO
|
||||
${OPENIMAGEIO_IMATH_TARGETS}
|
||||
)
|
||||
|
||||
# Unity builds: If in unity group mode, make the python bindings one group. If
|
||||
# in unity batch mode, use the smaller batch size because these tend to be
|
||||
# expensive files to compile.
|
||||
set_target_properties(PyOpenImageIO PROPERTIES
|
||||
UNITY_BUILD_BATCH_SIZE ${UNITY_SMALL_BATCH_SIZE})
|
||||
set_source_files_properties(${python_srcs} PROPERTIES
|
||||
UNITY_GROUP PyOpenImageIO)
|
||||
endif ()
|
||||
|
||||
# Nanobind-only backend: compile the py_*.cpp files in this directory
|
||||
# with nanobind and install as the main OpenImageIO Python package.
|
||||
# When backend is both, skip this; nanobind is built under src/python-nanobind/.
|
||||
if (OIIO_PYTHON_BINDINGS_BACKEND STREQUAL "nanobind")
|
||||
setup_python_module_nanobind (
|
||||
TARGET PyOpenImageIO
|
||||
MODULE OpenImageIO
|
||||
SOURCES py_oiio.cpp ${python_dual_backend_srcs}
|
||||
LIBS OpenImageIO
|
||||
)
|
||||
|
||||
target_compile_definitions (PyOpenImageIO
|
||||
PRIVATE OIIO_PY_BACKEND_NANOBIND)
|
||||
endif ()
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
# Nanobind migration status (vs `src/python` pybind11)
|
||||
# Nanobind migration status
|
||||
|
||||
Generated from the binding sources. The nanobind extension is `PyOpenImageIONanobind` / module `_OpenImageIO` (see `CMakeLists.txt`).
|
||||
Nanobind shares binding sources with pybind11 under `src/python/` (see
|
||||
`py_backend.h` and `python_dual_backend_srcs` in `CMakeLists.txt`). Configure
|
||||
with `-DOIIO_PYTHON_BINDINGS_BACKEND=nanobind` for nanobind-only (`PyOpenImageIO`
|
||||
/ module `OpenImageIO` in site-packages), or `both` to also build
|
||||
`PyOpenImageIONanobind` (`_OpenImageIO` under `lib/python/nanobind/OpenImageIO`).
|
||||
Nanobind-only code paths live in `py_oiio.h` / `py_oiio.cpp` behind
|
||||
`OIIO_PY_BACKEND_NANOBIND`. Shared Python↔C++ conversion helpers live in
|
||||
`py_oiio.h` for both backends.
|
||||
|
||||
## Migrated — full parity with pybind (no known gaps for this surface)
|
||||
|
||||
@@ -15,14 +22,14 @@ Generated from the binding sources. The nanobind extension is `PyOpenImageIONano
|
||||
|
||||
| Source file | Migrated (vs pybind) | Missing or divergent (vs pybind) |
|
||||
| --- | --- | --- |
|
||||
| `py_oiio.cpp` (`_OpenImageIO` module) | <ul><li><code>attribute</code> (one-arg and typed)</li><li><code>get_int_attribute</code></li><li><code>get_float_attribute</code></li><li><code>get_string_attribute</code></li><li><code>getattribute</code></li><li><code>__version__</code></li></ul> | <ul><li><code>geterror</code></li><li><code>get_bytes_attribute</code></li><li>Module <code>set_colorspace</code> (helper taking <code>ImageSpec</code> — the instance method is on <code>ImageSpec</code> in nanobind)</li><li><code>set_colorspace_rec709_gamma</code></li><li><code>equivalent_colorspace</code></li><li><code>is_imageio_format_name</code></li><li><code>AutoStride</code></li><li><code>openimageio_version</code>, <code>VERSION</code>, <code>VERSION_STRING</code>, <code>VERSION_MAJOR</code>, <code>VERSION_MINOR</code>, <code>VERSION_PATCH</code>, <code>INTRO_STRING</code></li><li>Optional: stack traces when <code>OPENIMAGEIO_DEBUG_PYTHON</code> is set (<code>Sysutil</code>)</li><li><code>make_pyobject</code>: no pybind-style <code>debugfmt</code> when the type is unhandled (returns default quietly)</li></ul> |
|
||||
| `__init__.py` (package) | Env / DLL path setup, <code>from ._OpenImageIO import *</code>, version docstring. | <strong>TODO:</strong> Python CLI entry-point trampolines when the install layout matches the full wheel. |
|
||||
| `py_oiio.cpp` (`OpenImageIO` module) | <ul><li><code>attribute</code> (one-arg and typed)</li><li><code>get_int_attribute</code></li><li><code>get_float_attribute</code></li><li><code>get_string_attribute</code></li><li><code>getattribute</code></li><li><code>__version__</code>, <code>VERSION_STRING</code></li></ul> | <ul><li><code>geterror</code></li><li><code>get_bytes_attribute</code></li><li>Module <code>set_colorspace</code> (helper taking <code>ImageSpec</code> — the instance method is on <code>ImageSpec</code> in nanobind)</li><li><code>set_colorspace_rec709_gamma</code></li><li><code>equivalent_colorspace</code></li><li><code>is_imageio_format_name</code></li><li><code>AutoStride</code></li><li><code>openimageio_version</code>, <code>VERSION</code>, <code>VERSION_MAJOR</code>, <code>VERSION_MINOR</code>, <code>VERSION_PATCH</code>, <code>INTRO_STRING</code></li><li>Optional: stack traces when <code>OPENIMAGEIO_DEBUG_PYTHON</code> is set (<code>Sysutil</code>)</li><li><code>make_pyobject</code>: no pybind-style <code>debugfmt</code> when the type is unhandled (returns default quietly)</li></ul> |
|
||||
| `__init__.py` (package) | Shared with pybind11: env / DLL path setup, <code>from .OpenImageIO import *</code>. | <strong>TODO:</strong> Python CLI entry-point trampolines when the install layout matches the full wheel. |
|
||||
|
||||
---
|
||||
|
||||
## Not migrated — entire pybind modules
|
||||
|
||||
These exist only under `src/python/` today; there are **no** corresponding `py_*.cpp` files in `src/python-nanobind/`.
|
||||
These exist only in the pybind11 module today (not in `python_dual_backend_srcs`).
|
||||
|
||||
| Source file | Python / C++ API |
|
||||
| --- | --- |
|
||||
@@ -0,0 +1,175 @@
|
||||
// Copyright Contributors to the OpenImageIO project.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// https://github.com/AcademySoftwareFoundation/OpenImageIO
|
||||
|
||||
#pragma once
|
||||
|
||||
#if defined(OIIO_PY_BACKEND_NANOBIND)
|
||||
|
||||
# include <nanobind/make_iterator.h>
|
||||
# include <nanobind/nanobind.h>
|
||||
# include <nanobind/ndarray.h>
|
||||
# include <nanobind/operators.h>
|
||||
# include <nanobind/stl/string.h>
|
||||
# include <nanobind/stl/vector.h>
|
||||
|
||||
namespace py = nanobind;
|
||||
using py_module = nanobind::module_;
|
||||
using namespace py::literals;
|
||||
|
||||
# define OIIO_PY_RW def_rw
|
||||
# define OIIO_PY_PROP_RO def_prop_ro
|
||||
# define OIIO_PY_PROP_RW def_prop_rw
|
||||
# define OIIO_PY_RO_STATIC def_prop_ro_static
|
||||
|
||||
namespace oiio_py {
|
||||
|
||||
inline std::string
|
||||
str_to_stdstring(const py::handle& handle)
|
||||
{
|
||||
return std::string(py::cast<py::str>(handle).c_str());
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline py::tuple
|
||||
make_tuple(size_t size, F&& fill)
|
||||
{
|
||||
py::list list;
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
list.append(fill(i));
|
||||
}
|
||||
return py::steal<py::tuple>(PyList_AsTuple(list.ptr()));
|
||||
}
|
||||
|
||||
inline constexpr auto ref_internal = py::rv_policy::reference_internal;
|
||||
|
||||
template<typename T>
|
||||
inline auto
|
||||
str(T&& x)
|
||||
{
|
||||
return std::forward<T>(x);
|
||||
}
|
||||
|
||||
inline void
|
||||
throw_key_error(const std::string& msg)
|
||||
{
|
||||
throw py::key_error(msg.c_str());
|
||||
}
|
||||
|
||||
inline std::string
|
||||
bytes_to_stdstring(const py::bytes& b)
|
||||
{
|
||||
return std::string(b.c_str(), b.size());
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
inline auto
|
||||
make_iterator(Container& container)
|
||||
{
|
||||
return py::make_iterator(py::type<Container>(), "iterator",
|
||||
container.begin(), container.end());
|
||||
}
|
||||
|
||||
// Return a py::object from a binding lambda (nanobind needs borrow).
|
||||
inline py::object
|
||||
return_object(py::object obj)
|
||||
{
|
||||
return py::borrow(obj);
|
||||
}
|
||||
|
||||
// TRANSFERS ownership of data; builds a 1D numpy array of length size.
|
||||
template<class T>
|
||||
inline py::object
|
||||
make_numpy_array(T* data, size_t size)
|
||||
{
|
||||
py::capsule owner(data, [](void* p) noexcept {
|
||||
delete[] reinterpret_cast<T*>(p);
|
||||
});
|
||||
py::ndarray<py::numpy, T, py::shape<-1>> array(data, { size }, owner);
|
||||
return py::cast(std::move(array), py::rv_policy::move);
|
||||
}
|
||||
|
||||
} // namespace oiio_py
|
||||
|
||||
#else // pybind11
|
||||
|
||||
# include <pybind11/numpy.h>
|
||||
# include <pybind11/operators.h>
|
||||
# include <pybind11/pybind11.h>
|
||||
|
||||
namespace py = pybind11;
|
||||
using py_module = pybind11::module;
|
||||
using namespace py::literals;
|
||||
|
||||
# define OIIO_PY_RW def_readwrite
|
||||
# define OIIO_PY_PROP_RO def_property_readonly
|
||||
# define OIIO_PY_PROP_RW def_property
|
||||
# define OIIO_PY_RO_STATIC def_property_readonly_static
|
||||
|
||||
namespace oiio_py {
|
||||
|
||||
inline std::string
|
||||
str_to_stdstring(const py::handle& handle)
|
||||
{
|
||||
return std::string(py::cast<py::str>(handle));
|
||||
}
|
||||
|
||||
template<typename F>
|
||||
inline py::tuple
|
||||
make_tuple(size_t size, F&& fill)
|
||||
{
|
||||
py::tuple result(size);
|
||||
for (size_t i = 0; i < size; ++i) {
|
||||
result[i] = fill(i);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
inline constexpr auto ref_internal = py::return_value_policy::reference_internal;
|
||||
|
||||
template<typename T>
|
||||
inline auto
|
||||
str(T&& x)
|
||||
{
|
||||
return py::str(std::forward<T>(x));
|
||||
}
|
||||
|
||||
inline void
|
||||
throw_key_error(const std::string& msg)
|
||||
{
|
||||
throw py::key_error(msg);
|
||||
}
|
||||
|
||||
inline std::string
|
||||
bytes_to_stdstring(const py::bytes& b)
|
||||
{
|
||||
return std::string(b);
|
||||
}
|
||||
|
||||
template<typename Container>
|
||||
inline auto
|
||||
make_iterator(Container& container)
|
||||
{
|
||||
return py::make_iterator(container.begin(), container.end());
|
||||
}
|
||||
|
||||
inline py::object
|
||||
return_object(py::object obj)
|
||||
{
|
||||
return obj;
|
||||
}
|
||||
|
||||
// TRANSFERS ownership of data; builds a 1D numpy array of length size.
|
||||
template<class T>
|
||||
inline py::object
|
||||
make_numpy_array(T* data, size_t size)
|
||||
{
|
||||
py::capsule free_when_done(data, [](void* f) {
|
||||
delete[] (reinterpret_cast<T*>(f));
|
||||
});
|
||||
return py::array_t<T>({ size }, { sizeof(T) }, data, free_when_done);
|
||||
}
|
||||
|
||||
} // namespace oiio_py
|
||||
|
||||
#endif
|
||||
+46
-40
@@ -56,44 +56,42 @@ ImageSpec_getattribute_typed(const ImageSpec& spec, const std::string& name,
|
||||
|
||||
|
||||
void
|
||||
declare_imagespec(py::module& m)
|
||||
declare_imagespec(py_module& m)
|
||||
{
|
||||
using namespace pybind11::literals;
|
||||
|
||||
py::class_<ImageSpec>(m, "ImageSpec")
|
||||
.def_readwrite("x", &ImageSpec::x)
|
||||
.def_readwrite("y", &ImageSpec::y)
|
||||
.def_readwrite("z", &ImageSpec::z)
|
||||
.def_readwrite("width", &ImageSpec::width)
|
||||
.def_readwrite("height", &ImageSpec::height)
|
||||
.def_readwrite("depth", &ImageSpec::depth)
|
||||
.def_readwrite("full_x", &ImageSpec::full_x)
|
||||
.def_readwrite("full_y", &ImageSpec::full_y)
|
||||
.def_readwrite("full_z", &ImageSpec::full_z)
|
||||
.def_readwrite("full_width", &ImageSpec::full_width)
|
||||
.def_readwrite("full_height", &ImageSpec::full_height)
|
||||
.def_readwrite("full_depth", &ImageSpec::full_depth)
|
||||
.def_readwrite("tile_width", &ImageSpec::tile_width)
|
||||
.def_readwrite("tile_height", &ImageSpec::tile_height)
|
||||
.def_readwrite("tile_depth", &ImageSpec::tile_depth)
|
||||
.def_readwrite("nchannels", &ImageSpec::nchannels)
|
||||
.def_readwrite("format", &ImageSpec::format)
|
||||
.def_property(
|
||||
.OIIO_PY_RW("x", &ImageSpec::x)
|
||||
.OIIO_PY_RW("y", &ImageSpec::y)
|
||||
.OIIO_PY_RW("z", &ImageSpec::z)
|
||||
.OIIO_PY_RW("width", &ImageSpec::width)
|
||||
.OIIO_PY_RW("height", &ImageSpec::height)
|
||||
.OIIO_PY_RW("depth", &ImageSpec::depth)
|
||||
.OIIO_PY_RW("full_x", &ImageSpec::full_x)
|
||||
.OIIO_PY_RW("full_y", &ImageSpec::full_y)
|
||||
.OIIO_PY_RW("full_z", &ImageSpec::full_z)
|
||||
.OIIO_PY_RW("full_width", &ImageSpec::full_width)
|
||||
.OIIO_PY_RW("full_height", &ImageSpec::full_height)
|
||||
.OIIO_PY_RW("full_depth", &ImageSpec::full_depth)
|
||||
.OIIO_PY_RW("tile_width", &ImageSpec::tile_width)
|
||||
.OIIO_PY_RW("tile_height", &ImageSpec::tile_height)
|
||||
.OIIO_PY_RW("tile_depth", &ImageSpec::tile_depth)
|
||||
.OIIO_PY_RW("nchannels", &ImageSpec::nchannels)
|
||||
.OIIO_PY_RW("format", &ImageSpec::format)
|
||||
.OIIO_PY_PROP_RW(
|
||||
"channelformats",
|
||||
[](const ImageSpec& spec) {
|
||||
return ImageSpec_get_channelformats(spec);
|
||||
},
|
||||
&ImageSpec_set_channelformats)
|
||||
.def_property("channelnames", &ImageSpec_get_channelnames,
|
||||
&ImageSpec_set_channelnames)
|
||||
.def_readwrite("alpha_channel", &ImageSpec::alpha_channel)
|
||||
.def_readwrite("z_channel", &ImageSpec::z_channel)
|
||||
.def_readwrite("deep", &ImageSpec::deep)
|
||||
.def_readwrite("extra_attribs", &ImageSpec::extra_attribs)
|
||||
.OIIO_PY_PROP_RW("channelnames", &ImageSpec_get_channelnames,
|
||||
&ImageSpec_set_channelnames)
|
||||
.OIIO_PY_RW("alpha_channel", &ImageSpec::alpha_channel)
|
||||
.OIIO_PY_RW("z_channel", &ImageSpec::z_channel)
|
||||
.OIIO_PY_RW("deep", &ImageSpec::deep)
|
||||
.OIIO_PY_RW("extra_attribs", &ImageSpec::extra_attribs)
|
||||
|
||||
.def_property("roi", &ImageSpec::roi, &ImageSpec::set_roi)
|
||||
.def_property("roi_full", &ImageSpec::roi_full,
|
||||
&ImageSpec::set_roi_full)
|
||||
.OIIO_PY_PROP_RW("roi", &ImageSpec::roi, &ImageSpec::set_roi)
|
||||
.OIIO_PY_PROP_RW("roi_full", &ImageSpec::roi_full,
|
||||
&ImageSpec::set_roi_full)
|
||||
|
||||
.def(py::init<>())
|
||||
.def(py::init<int, int, int, TypeDesc>())
|
||||
@@ -161,7 +159,7 @@ declare_imagespec(py::module& m)
|
||||
int chan) { return spec.channelformat(chan); })
|
||||
.def("channel_name",
|
||||
[](const ImageSpec& spec, int chan) {
|
||||
return PY_STR(std::string(spec.channel_name(chan)));
|
||||
return oiio_py::str(std::string(spec.channel_name(chan)));
|
||||
})
|
||||
.def("channelindex",
|
||||
[](const ImageSpec& spec, const std::string& name) {
|
||||
@@ -202,7 +200,7 @@ declare_imagespec(py::module& m)
|
||||
"get_string_attribute",
|
||||
[](const ImageSpec& spec, const std::string& name,
|
||||
const std::string& def) {
|
||||
return PY_STR(
|
||||
return oiio_py::str(
|
||||
std::string(spec.get_string_attribute(name, def)));
|
||||
},
|
||||
"name"_a, "defaultval"_a = "")
|
||||
@@ -210,8 +208,8 @@ declare_imagespec(py::module& m)
|
||||
"get_bytes_attribute",
|
||||
[](const ImageSpec& spec, const std::string& name,
|
||||
const std::string& def) {
|
||||
return py::bytes(
|
||||
std::string(spec.get_string_attribute(name, def)));
|
||||
std::string s(spec.get_string_attribute(name, def));
|
||||
return py::bytes(s.data(), s.size());
|
||||
},
|
||||
"name"_a, "defaultval"_a = "")
|
||||
.def("getattribute", &ImageSpec_getattribute_typed, "name"_a,
|
||||
@@ -221,7 +219,8 @@ declare_imagespec(py::module& m)
|
||||
[](const ImageSpec& self, const std::string& key, py::object def) {
|
||||
ParamValue tmpparam;
|
||||
auto p = self.find_attribute(key, tmpparam);
|
||||
return p ? make_pyobject(p->data(), p->type(), 1, def) : def;
|
||||
return p ? make_pyobject(p->data(), p->type(), 1, def)
|
||||
: oiio_py::return_object(def);
|
||||
},
|
||||
"key"_a, "default"_a = py::none())
|
||||
.def(
|
||||
@@ -235,7 +234,8 @@ declare_imagespec(py::module& m)
|
||||
.def_static(
|
||||
"metadata_val",
|
||||
[](const ParamValue& p, bool human) {
|
||||
return PY_STR(ImageSpec::metadata_val(p, human));
|
||||
return oiio_py::str(
|
||||
std::string(ImageSpec::metadata_val(p, human)));
|
||||
},
|
||||
"param"_a, "human"_a = false)
|
||||
.def(
|
||||
@@ -252,12 +252,17 @@ declare_imagespec(py::module& m)
|
||||
verb = ImageSpec::SerialDetailed;
|
||||
else if (Strutil::iequals(verbose, "detailedhuman"))
|
||||
verb = ImageSpec::SerialDetailedHuman;
|
||||
return PY_STR(spec.serialize(fmt, verb));
|
||||
return oiio_py::str(std::string(spec.serialize(fmt, verb)));
|
||||
},
|
||||
"format"_a = "text", "verbose"_a = "detailed")
|
||||
.def("to_xml",
|
||||
[](const ImageSpec& spec) { return PY_STR(spec.to_xml()); })
|
||||
.def("from_xml", &ImageSpec::from_xml)
|
||||
[](const ImageSpec& spec) {
|
||||
return oiio_py::str(std::string(spec.to_xml()));
|
||||
})
|
||||
.def("from_xml",
|
||||
[](ImageSpec& self, const std::string& xml) {
|
||||
self.from_xml(xml.c_str());
|
||||
})
|
||||
.def(
|
||||
"valid_tile_range",
|
||||
[](ImageSpec& self, int xbegin, int xend, int ybegin, int yend,
|
||||
@@ -280,7 +285,8 @@ declare_imagespec(py::module& m)
|
||||
ParamValue tmpparam;
|
||||
auto p = self.find_attribute(key, tmpparam);
|
||||
if (p == nullptr)
|
||||
throw py::key_error("key '" + key + "' does not exist");
|
||||
oiio_py::throw_key_error("key '" + key
|
||||
+ "' does not exist");
|
||||
return make_pyobject(p->data(), p->type());
|
||||
})
|
||||
// __setitem__ is the dict-like `ImageSpec[key] = value` assignment
|
||||
|
||||
+129
-45
@@ -6,6 +6,11 @@
|
||||
|
||||
#include <OpenImageIO/sysutil.h>
|
||||
|
||||
#if defined(OIIO_PY_BACKEND_NANOBIND)
|
||||
# include <OpenImageIO/oiioversion.h>
|
||||
# include <cstring>
|
||||
#endif
|
||||
|
||||
namespace PyOpenImageIO {
|
||||
|
||||
|
||||
@@ -66,6 +71,8 @@ typedesc_from_python_array_code(string_view code)
|
||||
|
||||
|
||||
|
||||
#ifndef OIIO_PY_BACKEND_NANOBIND
|
||||
|
||||
oiio_bufinfo::oiio_bufinfo(const py::buffer_info& pybuf)
|
||||
{
|
||||
if (pybuf.format.size())
|
||||
@@ -191,12 +198,17 @@ oiio_bufinfo::oiio_bufinfo(const py::buffer_info& pybuf, int nchans, int width,
|
||||
|
||||
|
||||
|
||||
#endif // !OIIO_PY_BACKEND_NANOBIND
|
||||
|
||||
|
||||
|
||||
py::object
|
||||
make_pyobject(const void* data, TypeDesc type, int nvalues,
|
||||
py::object defaultvalue)
|
||||
{
|
||||
if (!data || !nvalues)
|
||||
return defaultvalue;
|
||||
if (!data || !nvalues) {
|
||||
return oiio_py::return_object(defaultvalue);
|
||||
}
|
||||
if (type.basetype == TypeDesc::INT32)
|
||||
return C_to_val_or_tuple((const int*)data, type, nvalues);
|
||||
if (type.basetype == TypeDesc::FLOAT)
|
||||
@@ -223,16 +235,15 @@ make_pyobject(const void* data, TypeDesc type, int nvalues,
|
||||
// take possession of it.
|
||||
int n = type.arraylen * nvalues;
|
||||
if (n <= 0)
|
||||
return defaultvalue;
|
||||
uint8_t* ucdata(new uint8_t[n]);
|
||||
std::memcpy(ucdata, data, n);
|
||||
return make_numpy_array(ucdata, 1, 1, size_t(type.arraylen),
|
||||
size_t(nvalues));
|
||||
return oiio_py::return_object(defaultvalue);
|
||||
auto* copy = new uint8_t[n];
|
||||
std::memcpy(copy, data, static_cast<size_t>(n));
|
||||
return oiio_py::make_numpy_array(copy, static_cast<size_t>(n));
|
||||
}
|
||||
if (type.basetype == TypeDesc::UINT8)
|
||||
return C_to_val_or_tuple((const unsigned char*)data, type, nvalues);
|
||||
debugfmt("Don't know how to handle type {}\n", type);
|
||||
return defaultvalue;
|
||||
return oiio_py::return_object(defaultvalue);
|
||||
}
|
||||
|
||||
|
||||
@@ -240,7 +251,7 @@ make_pyobject(const void* data, TypeDesc type, int nvalues,
|
||||
static py::object
|
||||
oiio_getattribute_typed(const std::string& name, TypeDesc type = TypeUnknown)
|
||||
{
|
||||
if (type == TypeDesc::UNKNOWN)
|
||||
if (type == TypeUnknown)
|
||||
return py::none();
|
||||
char* data = OIIO_ALLOCA(char, type.size());
|
||||
if (!OIIO::getattribute(name, type, data))
|
||||
@@ -270,25 +281,21 @@ struct oiio_global_attrib_wrapper {
|
||||
};
|
||||
|
||||
|
||||
|
||||
// This OIIO_DECLARE_PYMODULE mojo is necessary if we want to pass in the
|
||||
// MODULE name as a #define. Google for Argument-Prescan for additional
|
||||
// info on why this is necessary
|
||||
|
||||
#define OIIO_DECLARE_PYMODULE(x) PYBIND11_MODULE(x, m)
|
||||
|
||||
OIIO_DECLARE_PYMODULE(PYMODULE_NAME)
|
||||
static void
|
||||
declare_global_bindings(py_module& m)
|
||||
{
|
||||
using namespace pybind11::literals;
|
||||
|
||||
if (Sysutil::getenv("OPENIMAGEIO_DEBUG_PYTHON") != "")
|
||||
Sysutil::setup_crash_stacktrace("stdout");
|
||||
|
||||
// Basic helper classes
|
||||
declare_typedesc(m);
|
||||
declare_paramvalue(m);
|
||||
declare_imagespec(m);
|
||||
declare_roi(m);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifndef OIIO_PY_BACKEND_NANOBIND
|
||||
static void
|
||||
declare_pybind_bindings(py_module& m)
|
||||
{
|
||||
declare_deepdata(m);
|
||||
declare_colorconfig(m);
|
||||
|
||||
@@ -306,9 +313,14 @@ OIIO_DECLARE_PYMODULE(PYMODULE_NAME)
|
||||
declare_texturesystem(m);
|
||||
|
||||
declare_imagebufalgo(m);
|
||||
}
|
||||
#endif
|
||||
|
||||
// Global (OpenImageIO scope) functions and symbols
|
||||
m.def("geterror", &OIIO::geterror, "clear"_a = true);
|
||||
|
||||
|
||||
static void
|
||||
declare_global_attribute_functions(py_module& m)
|
||||
{
|
||||
m.def("attribute", [](const std::string& name, const py::object& obj) {
|
||||
oiio_global_attrib_wrapper wrapper;
|
||||
attribute_onearg(wrapper, name, obj);
|
||||
@@ -324,55 +336,127 @@ OIIO_DECLARE_PYMODULE(PYMODULE_NAME)
|
||||
[](const std::string& name, int def) {
|
||||
return OIIO::get_int_attribute(name, def);
|
||||
},
|
||||
py::arg("name"), py::arg("defaultval") = 0);
|
||||
"name"_a, "defaultval"_a = 0);
|
||||
m.def(
|
||||
"get_float_attribute",
|
||||
[](const std::string& name, float def) {
|
||||
return OIIO::get_float_attribute(name, def);
|
||||
},
|
||||
py::arg("name"), py::arg("defaultval") = 0.0f);
|
||||
"name"_a, "defaultval"_a = 0.0f);
|
||||
m.def(
|
||||
"get_string_attribute",
|
||||
[](const std::string& name, const std::string& def) {
|
||||
return PY_STR(std::string(OIIO::get_string_attribute(name, def)));
|
||||
return oiio_py::str(
|
||||
std::string(OIIO::get_string_attribute(name, def)));
|
||||
},
|
||||
py::arg("name"), py::arg("defaultval") = "");
|
||||
"name"_a, "defaultval"_a = "");
|
||||
m.def("getattribute", &oiio_getattribute_typed, "name"_a,
|
||||
"type"_a = TypeUnknown);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifndef OIIO_PY_BACKEND_NANOBIND
|
||||
static void
|
||||
declare_pybind_global_functions(py_module& m)
|
||||
{
|
||||
m.def("geterror", &OIIO::geterror, "clear"_a = true);
|
||||
m.def(
|
||||
"get_bytes_attribute",
|
||||
[](const std::string& name, const std::string& def) {
|
||||
return py::bytes(
|
||||
std::string(OIIO::get_string_attribute(name, def)));
|
||||
std::string s(OIIO::get_string_attribute(name, def));
|
||||
return py::bytes(s.data(), s.size());
|
||||
},
|
||||
py::arg("name"), py::arg("defaultval") = "");
|
||||
m.def("getattribute", &oiio_getattribute_typed);
|
||||
"name"_a, "defaultval"_a = "");
|
||||
m.def(
|
||||
"set_colorspace",
|
||||
[](ImageSpec& spec, const std::string& name) {
|
||||
set_colorspace(spec, name);
|
||||
},
|
||||
py::arg("spec"), py::arg("name"));
|
||||
m.def("set_colorspace_rec709_gamma", [](ImageSpec& spec, float gamma) {
|
||||
set_colorspace_rec709_gamma(spec, gamma);
|
||||
});
|
||||
m.def("equivalent_colorspace",
|
||||
[](const std::string& a, const std::string& b) {
|
||||
return equivalent_colorspace(a, b);
|
||||
});
|
||||
"spec"_a, "name"_a);
|
||||
m.def(
|
||||
"set_colorspace_rec709_gamma",
|
||||
[](ImageSpec& spec, float gamma) {
|
||||
set_colorspace_rec709_gamma(spec, gamma);
|
||||
},
|
||||
"spec"_a, "gamma"_a);
|
||||
m.def(
|
||||
"equivalent_colorspace",
|
||||
[](const std::string& a, const std::string& b) {
|
||||
return equivalent_colorspace(a, b);
|
||||
},
|
||||
"a"_a, "b"_a);
|
||||
m.def(
|
||||
"is_imageio_format_name",
|
||||
[](const std::string& name) {
|
||||
return OIIO::is_imageio_format_name(name);
|
||||
},
|
||||
py::arg("name"));
|
||||
"name"_a);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
static void
|
||||
declare_module_attributes(py_module& m)
|
||||
{
|
||||
#if defined(OIIO_PY_BACKEND_NANOBIND)
|
||||
m.attr("__version__") = OIIO_VERSION_STRING;
|
||||
m.attr("VERSION_STRING") = OIIO_VERSION_STRING;
|
||||
#else
|
||||
m.attr("AutoStride") = AutoStride;
|
||||
m.attr("openimageio_version") = OIIO_VERSION;
|
||||
m.attr("VERSION") = OIIO_VERSION;
|
||||
m.attr("VERSION_STRING") = PY_STR(OIIO_VERSION_STRING);
|
||||
m.attr("VERSION_STRING") = oiio_py::str(OIIO_VERSION_STRING);
|
||||
m.attr("VERSION_MAJOR") = OIIO_VERSION_MAJOR;
|
||||
m.attr("VERSION_MINOR") = OIIO_VERSION_MINOR;
|
||||
m.attr("VERSION_PATCH") = OIIO_VERSION_PATCH;
|
||||
m.attr("INTRO_STRING") = PY_STR(OIIO_INTRO_STRING);
|
||||
m.attr("__version__") = PY_STR(OIIO_VERSION_STRING);
|
||||
m.attr("INTRO_STRING") = oiio_py::str(OIIO_INTRO_STRING);
|
||||
m.attr("__version__") = oiio_py::str(OIIO_VERSION_STRING);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if defined(OIIO_PY_BACKEND_NANOBIND)
|
||||
|
||||
} // namespace PyOpenImageIO
|
||||
|
||||
# define OIIO_DECLARE_NB_MODULE(x) NB_MODULE(x, m)
|
||||
|
||||
# if defined(OIIO_PY_NANOBIND_ISOLATED_PACKAGE)
|
||||
OIIO_DECLARE_NB_MODULE(_OpenImageIO)
|
||||
# else
|
||||
OIIO_DECLARE_NB_MODULE(OpenImageIO)
|
||||
# endif
|
||||
{
|
||||
m.doc() = "OpenImageIO nanobind bindings.";
|
||||
|
||||
PyOpenImageIO::declare_global_bindings(m);
|
||||
PyOpenImageIO::declare_global_attribute_functions(m);
|
||||
PyOpenImageIO::declare_module_attributes(m);
|
||||
}
|
||||
|
||||
#else // pybind11
|
||||
|
||||
// This OIIO_DECLARE_PYMODULE mojo is necessary if we want to pass in the
|
||||
// MODULE name as a #define. Google for Argument-Prescan for additional
|
||||
// info on why this is necessary
|
||||
|
||||
# define OIIO_DECLARE_PYMODULE(x) PYBIND11_MODULE(x, m)
|
||||
|
||||
OIIO_DECLARE_PYMODULE(PYMODULE_NAME)
|
||||
{
|
||||
if (Sysutil::getenv("OPENIMAGEIO_DEBUG_PYTHON") != "")
|
||||
Sysutil::setup_crash_stacktrace("stdout");
|
||||
|
||||
declare_global_bindings(m);
|
||||
declare_pybind_bindings(m);
|
||||
declare_global_attribute_functions(m);
|
||||
declare_pybind_global_functions(m);
|
||||
declare_module_attributes(m);
|
||||
}
|
||||
|
||||
} // namespace PyOpenImageIO
|
||||
|
||||
#endif // OIIO_PY_BACKEND_NANOBIND
|
||||
|
||||
+227
-122
@@ -38,18 +38,27 @@
|
||||
#include <OpenImageIO/texture.h>
|
||||
#include <OpenImageIO/typedesc.h>
|
||||
|
||||
#include <pybind11/numpy.h>
|
||||
#include <pybind11/operators.h>
|
||||
#include <pybind11/pybind11.h>
|
||||
#include <pybind11/stl.h>
|
||||
namespace py = pybind11;
|
||||
#if defined(OIIO_PY_BACKEND_NANOBIND)
|
||||
# include <cstring>
|
||||
#endif
|
||||
|
||||
#include "py_backend.h"
|
||||
|
||||
#if defined(OIIO_PY_BACKEND_NANOBIND)
|
||||
# define PY_STR(x) oiio_py::str(x)
|
||||
|
||||
#else
|
||||
# include <pybind11/numpy.h>
|
||||
# include <pybind11/stl.h>
|
||||
|
||||
// Python3 is always unicode, so return a true str
|
||||
#define PY_STR py::str
|
||||
# define PY_STR py::str
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
#ifndef OIIO_PY_BACKEND_NANOBIND
|
||||
namespace pybind11 {
|
||||
namespace detail {
|
||||
|
||||
@@ -76,6 +85,7 @@ namespace detail {
|
||||
|
||||
} // namespace detail
|
||||
} // namespace pybind11
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -85,23 +95,23 @@ using namespace OIIO;
|
||||
|
||||
// clang-format off
|
||||
|
||||
void declare_imagespec (py::module& m);
|
||||
void declare_imageinput (py::module& m);
|
||||
void declare_imageoutput (py::module& m);
|
||||
void declare_typedesc (py::module& m);
|
||||
void declare_roi (py::module& m);
|
||||
void declare_deepdata (py::module& m);
|
||||
void declare_colorconfig (py::module& m);
|
||||
void declare_imagecache (py::module& m);
|
||||
void declare_imagebuf (py::module& m);
|
||||
void declare_imagebufalgo (py::module& m);
|
||||
void declare_paramvalue (py::module& m);
|
||||
void declare_global (py::module& m);
|
||||
void declare_wrap (py::module& m);
|
||||
void declare_mipmpode (py::module& m);
|
||||
void declare_interpmode (py::module& m);
|
||||
void declare_textureopt (py::module& m);
|
||||
void declare_texturesystem (py::module& m);
|
||||
void declare_imagespec (py_module& m);
|
||||
void declare_imageinput (py_module& m);
|
||||
void declare_imageoutput (py_module& m);
|
||||
void declare_typedesc (py_module& m);
|
||||
void declare_roi (py_module& m);
|
||||
void declare_deepdata (py_module& m);
|
||||
void declare_colorconfig (py_module& m);
|
||||
void declare_imagecache (py_module& m);
|
||||
void declare_imagebuf (py_module& m);
|
||||
void declare_imagebufalgo (py_module& m);
|
||||
void declare_paramvalue (py_module& m);
|
||||
void declare_global (py_module& m);
|
||||
void declare_wrap (py_module& m);
|
||||
void declare_mipmpode (py_module& m);
|
||||
void declare_interpmode (py_module& m);
|
||||
void declare_textureopt (py_module& m);
|
||||
void declare_texturesystem (py_module& m);
|
||||
|
||||
// bool PyProgressCallback(void*, float);
|
||||
// object C_array_to_Python_array (const char *data, TypeDesc type, size_t size);
|
||||
@@ -109,12 +119,13 @@ TypeDesc typedesc_from_python_array_code (string_view code);
|
||||
// const char * python_array_code (TypeDesc format); // unused
|
||||
|
||||
|
||||
#ifndef OIIO_PY_BACKEND_NANOBIND
|
||||
inline std::string
|
||||
object_classname(const py::object& obj)
|
||||
{
|
||||
return obj.attr("__class__").attr("__name__").cast<py::str>();
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
template<typename T> struct PyTypeForCType { };
|
||||
@@ -129,12 +140,12 @@ template<> struct PyTypeForCType<uint64_t> { typedef py::int_ type; };
|
||||
template<> struct PyTypeForCType<float> { typedef py::float_ type; };
|
||||
template<> struct PyTypeForCType<half> { typedef py::float_ type; };
|
||||
template<> struct PyTypeForCType<double> { typedef py::float_ type; };
|
||||
template<> struct PyTypeForCType<const char*> { typedef PY_STR type; };
|
||||
template<> struct PyTypeForCType<std::string> { typedef PY_STR type; };
|
||||
template<> struct PyTypeForCType<const char*> { typedef py::str type; };
|
||||
template<> struct PyTypeForCType<std::string> { typedef py::str type; };
|
||||
|
||||
// clang-format on
|
||||
|
||||
|
||||
#ifndef OIIO_PY_BACKEND_NANOBIND
|
||||
// Struct that holds OIIO style buffer info, constructed from
|
||||
// py::buffer_info
|
||||
struct oiio_bufinfo {
|
||||
@@ -155,6 +166,7 @@ struct oiio_bufinfo {
|
||||
// Retrieve presumed contiguous data value index i.
|
||||
template<typename T> T dataval(size_t i) { return ((const T*)data)[i]; }
|
||||
};
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -173,13 +185,13 @@ py_indexable_pod_to_stdvector(std::vector<T>& vals, const PYT& obj)
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
auto elem = obj[i];
|
||||
if (std::is_same<T, float>::value && py::isinstance<py::float_>(elem)) {
|
||||
vals.emplace_back(T(elem.template cast<float>()));
|
||||
vals.emplace_back(T(py::cast<float>(elem)));
|
||||
} else if ((std::is_same<T, float>::value || std::is_same<T, int>::value)
|
||||
&& py::isinstance<py::int_>(elem)) {
|
||||
vals.emplace_back(T(elem.template cast<int>()));
|
||||
vals.emplace_back(T(py::cast<int>(elem)));
|
||||
} else if (std::is_same<T, unsigned int>::value
|
||||
&& py::isinstance<py::int_>(elem)) {
|
||||
vals.emplace_back(T(elem.template cast<unsigned int>()));
|
||||
vals.emplace_back(T(py::cast<unsigned int>(elem)));
|
||||
} else {
|
||||
// FIXME? Other cases?
|
||||
vals.emplace_back(T(42));
|
||||
@@ -203,7 +215,7 @@ py_indexable_pod_to_stdvector(std::vector<std::string>& vals, const PYT& obj)
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
auto elem = obj[i];
|
||||
if (py::isinstance<py::str>(elem)) {
|
||||
vals.emplace_back(elem.template cast<py::str>());
|
||||
vals.emplace_back(oiio_py::str_to_stdstring(elem));
|
||||
} else {
|
||||
// FIXME? Other cases?
|
||||
vals.emplace_back("");
|
||||
@@ -227,12 +239,11 @@ py_indexable_pod_to_stdvector(std::vector<TypeDesc>& vals, const PYT& obj)
|
||||
for (size_t i = 0; i < length; ++i) {
|
||||
auto elem = obj[i];
|
||||
if (py::isinstance<TypeDesc>(elem)) {
|
||||
vals.emplace_back(*(elem.template cast<TypeDesc*>()));
|
||||
vals.emplace_back(py::cast<TypeDesc>(elem));
|
||||
} else if (py::isinstance<TypeDesc::BASETYPE>(elem)) {
|
||||
vals.emplace_back(*(elem.template cast<TypeDesc::BASETYPE*>()));
|
||||
vals.emplace_back(TypeDesc(py::cast<TypeDesc::BASETYPE>(elem)));
|
||||
} else if (py::isinstance<py::str>(elem)) {
|
||||
vals.emplace_back(
|
||||
TypeDesc(std::string(elem.template cast<py::str>())));
|
||||
vals.emplace_back(TypeDesc(oiio_py::str_to_stdstring(elem)));
|
||||
} else {
|
||||
// FIXME? Other cases?
|
||||
vals.emplace_back(TypeUnknown);
|
||||
@@ -251,7 +262,7 @@ py_scalar_pod_to_stdvector(std::vector<T>& vals, const py::object& obj)
|
||||
using pytype = typename PyTypeForCType<T>::type;
|
||||
vals.clear();
|
||||
if (py::isinstance<pytype>(obj)) {
|
||||
vals.emplace_back(obj.template cast<pytype>());
|
||||
vals.emplace_back(py::cast<T>(obj));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -265,10 +276,10 @@ py_scalar_pod_to_stdvector(std::vector<float>& vals, const py::object& obj)
|
||||
{
|
||||
vals.clear();
|
||||
if (py::isinstance<py::float_>(obj)) {
|
||||
vals.emplace_back(obj.template cast<py::float_>());
|
||||
vals.emplace_back(py::cast<float>(obj));
|
||||
return true;
|
||||
} else if (py::isinstance<py::int_>(obj)) {
|
||||
int i = obj.template cast<py::int_>();
|
||||
int i = py::cast<int>(obj);
|
||||
vals.emplace_back((float)i);
|
||||
return true;
|
||||
} else {
|
||||
@@ -283,13 +294,13 @@ py_scalar_pod_to_stdvector(std::vector<TypeDesc>& vals, const py::object& obj)
|
||||
{
|
||||
vals.clear();
|
||||
if (py::isinstance<TypeDesc>(obj)) {
|
||||
vals.emplace_back(*(obj.template cast<TypeDesc*>()));
|
||||
vals.emplace_back(py::cast<TypeDesc>(obj));
|
||||
return true;
|
||||
} else if (py::isinstance<TypeDesc::BASETYPE>(obj)) {
|
||||
vals.emplace_back(*(obj.template cast<TypeDesc::BASETYPE*>()));
|
||||
vals.emplace_back(TypeDesc(py::cast<TypeDesc::BASETYPE>(obj)));
|
||||
return true;
|
||||
} else if (py::isinstance<py::str>(obj)) {
|
||||
vals.emplace_back(TypeDesc(std::string(obj.template cast<py::str>())));
|
||||
vals.emplace_back(TypeDesc(oiio_py::str_to_stdstring(obj)));
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
@@ -298,30 +309,33 @@ py_scalar_pod_to_stdvector(std::vector<TypeDesc>& vals, const py::object& obj)
|
||||
|
||||
|
||||
|
||||
// Shared: read `count` contiguous elements of `format` into vector<T>.
|
||||
// Matches the element-dispatch logic from the original pybind11 implementation.
|
||||
template<typename T>
|
||||
inline bool
|
||||
py_buffer_to_stdvector(std::vector<T>& vals, const py::buffer& obj)
|
||||
buffer_format_to_stdvector(std::vector<T>& vals, TypeDesc format,
|
||||
const void* data, size_t count)
|
||||
{
|
||||
OIIO_DASSERT(py::isinstance<py::buffer>(obj));
|
||||
oiio_bufinfo binfo(obj.request());
|
||||
bool ok = true;
|
||||
vals.reserve(binfo.size);
|
||||
for (size_t i = 0; i < binfo.size; ++i) {
|
||||
vals.reserve(count);
|
||||
const unsigned char* bytes = static_cast<const unsigned char*>(data);
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
if (std::is_same<T, float>::value
|
||||
&& binfo.format.basetype == TypeDesc::FLOAT) {
|
||||
vals.push_back(binfo.dataval<float>(i));
|
||||
&& format.basetype == TypeDesc::FLOAT) {
|
||||
vals.push_back(reinterpret_cast<const float*>(bytes)[i]);
|
||||
} else if ((std::is_same<T, float>::value || std::is_same<T, int>::value)
|
||||
&& binfo.format.basetype == TypeDesc::INT) {
|
||||
vals.push_back(T(binfo.dataval<int>(i)));
|
||||
&& format.basetype == TypeDesc::INT) {
|
||||
vals.push_back(T(reinterpret_cast<const int*>(bytes)[i]));
|
||||
} else if (std::is_same<T, unsigned int>::value
|
||||
&& binfo.format.basetype == TypeDesc::UINT) {
|
||||
vals.push_back(T(binfo.dataval<unsigned int>(i)));
|
||||
&& format.basetype == TypeDesc::UINT) {
|
||||
vals.push_back(T(reinterpret_cast<const unsigned int*>(bytes)[i]));
|
||||
} else if (std::is_same<T, unsigned char>::value
|
||||
&& binfo.format.basetype == TypeDesc::UINT8) {
|
||||
vals.push_back(T(binfo.dataval<unsigned char>(i)));
|
||||
&& format.basetype == TypeDesc::UINT8) {
|
||||
vals.push_back(T(reinterpret_cast<const unsigned char*>(bytes)[i]));
|
||||
} else if (std::is_same<T, unsigned short>::value
|
||||
&& binfo.format.basetype == TypeDesc::UINT16) {
|
||||
vals.push_back(T(binfo.dataval<unsigned short>(i)));
|
||||
&& format.basetype == TypeDesc::UINT16) {
|
||||
vals.push_back(
|
||||
T(reinterpret_cast<const unsigned short*>(bytes)[i]));
|
||||
} else {
|
||||
// FIXME? Other cases?
|
||||
vals.push_back(T(42));
|
||||
@@ -332,6 +346,67 @@ py_buffer_to_stdvector(std::vector<T>& vals, const py::buffer& obj)
|
||||
}
|
||||
|
||||
|
||||
|
||||
#if defined(OIIO_PY_BACKEND_NANOBIND)
|
||||
template<typename T>
|
||||
inline bool
|
||||
py_buffer_to_stdvector(std::vector<T>& vals, const py::object& obj)
|
||||
{
|
||||
Py_buffer view;
|
||||
if (PyObject_GetBuffer(obj.ptr(), &view, PyBUF_FORMAT | PyBUF_C_CONTIGUOUS)
|
||||
!= 0) {
|
||||
PyErr_Clear();
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ok = view.itemsize > 0 && view.len % view.itemsize == 0;
|
||||
if (!ok) {
|
||||
PyBuffer_Release(&view);
|
||||
return false;
|
||||
}
|
||||
|
||||
TypeDesc format = TypeUnknown;
|
||||
if (view.format && view.format[0]) {
|
||||
format = typedesc_from_python_array_code(view.format);
|
||||
}
|
||||
|
||||
const size_t count = static_cast<size_t>(view.len) / view.itemsize;
|
||||
ok = buffer_format_to_stdvector(vals, format, view.buf, count);
|
||||
PyBuffer_Release(&view);
|
||||
return ok;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
inline bool
|
||||
py_buffer_to_stdvector(std::vector<std::string>&, const py::object&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<>
|
||||
inline bool
|
||||
py_buffer_to_stdvector(std::vector<TypeDesc>&, const py::object&)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
template<typename T>
|
||||
inline bool
|
||||
py_buffer_to_stdvector(std::vector<T>& vals, const py::buffer& obj)
|
||||
{
|
||||
OIIO_DASSERT(py::isinstance<py::buffer>(obj));
|
||||
oiio_bufinfo binfo(obj.request());
|
||||
return buffer_format_to_stdvector(vals, binfo.format, binfo.data,
|
||||
binfo.size);
|
||||
}
|
||||
|
||||
|
||||
// Specialization for reading strings
|
||||
template<>
|
||||
inline bool
|
||||
@@ -351,6 +426,8 @@ py_buffer_to_stdvector(std::vector<TypeDesc>& /*vals*/,
|
||||
return false; // not supported
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
// Suck up a tuple (or list, or single instance) of presumed T values into a
|
||||
@@ -361,16 +438,22 @@ inline bool
|
||||
py_to_stdvector(std::vector<T>& vals, const py::object& obj)
|
||||
{
|
||||
if (py::isinstance<py::tuple>(obj)) { // if it's a Python tuple
|
||||
return py_indexable_pod_to_stdvector(vals, obj.cast<py::tuple>());
|
||||
return py_indexable_pod_to_stdvector(vals, py::cast<py::tuple>(obj));
|
||||
}
|
||||
if (py::isinstance<py::list>(obj)) { // if it's a Python list
|
||||
return py_indexable_pod_to_stdvector(vals, obj.cast<py::list>());
|
||||
return py_indexable_pod_to_stdvector(vals, py::cast<py::list>(obj));
|
||||
}
|
||||
// Apparently a str can masquerade as a buffer object, so make sure to
|
||||
// exclude that from the buffer case.
|
||||
if (py::isinstance<py::buffer>(obj) && !py::isinstance<py::str>(obj)) {
|
||||
return py_buffer_to_stdvector(vals, obj.cast<py::buffer>());
|
||||
#if defined(OIIO_PY_BACKEND_NANOBIND)
|
||||
if (PyObject_CheckBuffer(obj.ptr()) && !PyUnicode_Check(obj.ptr())) {
|
||||
return py_buffer_to_stdvector(vals, obj);
|
||||
}
|
||||
#else
|
||||
if (py::isinstance<py::buffer>(obj) && !py::isinstance<py::str>(obj)) {
|
||||
return py_buffer_to_stdvector(vals, py::cast<py::buffer>(obj));
|
||||
}
|
||||
#endif
|
||||
|
||||
// handle scalar case or bust
|
||||
return py_scalar_pod_to_stdvector(vals, obj);
|
||||
@@ -382,23 +465,22 @@ template<typename T>
|
||||
inline py::tuple
|
||||
C_to_tuple(cspan<T> vals)
|
||||
{
|
||||
size_t size = vals.size();
|
||||
py::tuple result(size);
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
result[i] = typename PyTypeForCType<T>::type(vals[i]);
|
||||
return result;
|
||||
return oiio_py::make_tuple(vals.size(), [&](size_t i) {
|
||||
if constexpr (std::is_same_v<T, half>) {
|
||||
return py::cast(static_cast<float>(vals[i]));
|
||||
} else {
|
||||
return py::cast(vals[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
template<typename T>
|
||||
inline py::tuple
|
||||
C_to_tuple(span<T> vals)
|
||||
{
|
||||
size_t size = vals.size();
|
||||
py::tuple result(size);
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
result[i] = typename PyTypeForCType<T>::type(vals[i]);
|
||||
return result;
|
||||
return C_to_tuple(cspan<T>(vals));
|
||||
}
|
||||
|
||||
|
||||
@@ -406,10 +488,13 @@ template<typename T>
|
||||
inline py::tuple
|
||||
C_to_tuple(const T* vals, size_t size)
|
||||
{
|
||||
py::tuple result(size);
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
result[i] = typename PyTypeForCType<T>::type(vals[i]);
|
||||
return result;
|
||||
return oiio_py::make_tuple(size, [&](size_t i) {
|
||||
if constexpr (std::is_same_v<T, half>) {
|
||||
return py::cast(static_cast<float>(vals[i]));
|
||||
} else {
|
||||
return py::cast(vals[i]);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -417,11 +502,9 @@ template<>
|
||||
inline py::tuple
|
||||
C_to_tuple(cspan<unsigned char> vals)
|
||||
{
|
||||
size_t size = vals.size();
|
||||
py::tuple result(size);
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
result[i] = static_cast<unsigned char>(vals[i]);
|
||||
return result;
|
||||
return oiio_py::make_tuple(vals.size(), [&](size_t i) {
|
||||
return static_cast<unsigned char>(vals[i]);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -430,11 +513,8 @@ template<>
|
||||
inline py::tuple
|
||||
C_to_tuple<TypeDesc>(cspan<TypeDesc> vals)
|
||||
{
|
||||
size_t size = vals.size();
|
||||
py::tuple result(size);
|
||||
for (size_t i = 0; i < size; ++i)
|
||||
result[i] = py::cast(vals[i]);
|
||||
return result;
|
||||
return oiio_py::make_tuple(vals.size(),
|
||||
[&](size_t i) { return py::cast(vals[i]); });
|
||||
}
|
||||
|
||||
|
||||
@@ -448,10 +528,15 @@ C_to_val_or_tuple(const T* vals, TypeDesc type, int nvalues = 1)
|
||||
{
|
||||
OIIO_DASSERT(vals && nvalues);
|
||||
size_t n = type.numelements() * type.aggregate * nvalues;
|
||||
if (n == 1 && !type.arraylen)
|
||||
return typename PyTypeForCType<T>::type(vals[0]);
|
||||
else
|
||||
if (n == 1 && !type.arraylen) {
|
||||
if constexpr (std::is_same_v<T, half>) {
|
||||
return py::cast(static_cast<float>(vals[0]));
|
||||
} else {
|
||||
return py::cast(vals[0]);
|
||||
}
|
||||
} else {
|
||||
return C_to_tuple(cspan<T>(vals, n));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -464,32 +549,36 @@ attribute_typed(T& myobj, string_view name, TypeDesc type, const POBJ& dataobj)
|
||||
std::vector<int> vals;
|
||||
bool ok = py_to_stdvector(vals, dataobj);
|
||||
ok &= (vals.size() == type.numelements() * type.aggregate);
|
||||
if (ok)
|
||||
if (ok) {
|
||||
myobj.attribute(name, type, &vals[0]);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
if (type.basetype == TypeDesc::UINT) {
|
||||
std::vector<unsigned int> vals;
|
||||
bool ok = py_to_stdvector(vals, dataobj);
|
||||
ok &= (vals.size() == type.numelements() * type.aggregate);
|
||||
if (ok)
|
||||
if (ok) {
|
||||
myobj.attribute(name, type, &vals[0]);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
if (type.basetype == TypeDesc::UINT8) {
|
||||
std::vector<unsigned char> vals;
|
||||
bool ok = py_to_stdvector(vals, dataobj);
|
||||
ok &= (vals.size() == type.numelements() * type.aggregate);
|
||||
if (ok)
|
||||
if (ok) {
|
||||
myobj.attribute(name, type, &vals[0]);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
if (type.basetype == TypeDesc::FLOAT) {
|
||||
std::vector<float> vals;
|
||||
bool ok = py_to_stdvector(vals, dataobj);
|
||||
ok &= (vals.size() == type.numelements() * type.aggregate);
|
||||
if (ok)
|
||||
if (ok) {
|
||||
myobj.attribute(name, type, &vals[0]);
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
if (type.basetype == TypeDesc::STRING) {
|
||||
@@ -498,8 +587,9 @@ attribute_typed(T& myobj, string_view name, TypeDesc type, const POBJ& dataobj)
|
||||
ok &= (vals.size() == type.numelements() * type.aggregate);
|
||||
if (ok) {
|
||||
std::vector<ustring> u;
|
||||
for (auto& val : vals)
|
||||
for (auto& val : vals) {
|
||||
u.emplace_back(val);
|
||||
}
|
||||
myobj.attribute(name, type, &u[0]);
|
||||
}
|
||||
return ok;
|
||||
@@ -515,16 +605,18 @@ template<typename T>
|
||||
inline void
|
||||
attribute_onearg(T& myobj, string_view name, const py::object& obj)
|
||||
{
|
||||
if (py::isinstance<py::float_>(obj))
|
||||
myobj.attribute(name, float(obj.template cast<py::float_>()));
|
||||
else if (py::isinstance<py::int_>(obj))
|
||||
myobj.attribute(name, int(obj.template cast<py::int_>()));
|
||||
else if (py::isinstance<py::str>(obj))
|
||||
myobj.attribute(name, std::string(obj.template cast<py::str>()));
|
||||
else if (py::isinstance<py::bytes>(obj))
|
||||
myobj.attribute(name, std::string(obj.template cast<py::bytes>()));
|
||||
else
|
||||
if (py::isinstance<py::float_>(obj)) {
|
||||
myobj.attribute(name, py::cast<float>(obj));
|
||||
} else if (py::isinstance<py::int_>(obj)) {
|
||||
myobj.attribute(name, py::cast<int>(obj));
|
||||
} else if (py::isinstance<py::str>(obj)) {
|
||||
myobj.attribute(name, oiio_py::str_to_stdstring(obj));
|
||||
} else if (py::isinstance<py::bytes>(obj)) {
|
||||
myobj.attribute(name,
|
||||
oiio_py::bytes_to_stdstring(py::cast<py::bytes>(obj)));
|
||||
} else {
|
||||
throw py::type_error("attribute() value must be int, float, or str");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -542,17 +634,20 @@ py::object
|
||||
getattribute_typed(const T& obj, const std::string& name,
|
||||
TypeDesc type = TypeUnknown)
|
||||
{
|
||||
if (type == TypeUnknown)
|
||||
if (type == TypeUnknown) {
|
||||
return py::none();
|
||||
}
|
||||
char* data = OIIO_ALLOCA(char, type.size());
|
||||
bool ok = obj.getattribute(name, type, data);
|
||||
if (!ok)
|
||||
if (!ok) {
|
||||
return py::none(); // None
|
||||
}
|
||||
return make_pyobject(data, type);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifndef OIIO_PY_BACKEND_NANOBIND
|
||||
// TRANSFERS ownership of the data pointer!
|
||||
// N.B. There is some evidence that this doesn't work properly with
|
||||
// non-float arrays. Maybe a limitation of pybind11?
|
||||
@@ -597,33 +692,43 @@ inline py::object
|
||||
make_numpy_array(TypeDesc format, void* data, int dims, size_t chans,
|
||||
size_t width, size_t height, size_t depth = 1)
|
||||
{
|
||||
if (format == TypeDesc::FLOAT)
|
||||
if (format == TypeDesc::FLOAT) {
|
||||
return make_numpy_array((float*)data, dims, chans, width, height,
|
||||
depth);
|
||||
if (format == TypeDesc::UINT8)
|
||||
}
|
||||
if (format == TypeDesc::UINT8) {
|
||||
return make_numpy_array((unsigned char*)data, dims, chans, width,
|
||||
height, depth);
|
||||
if (format == TypeDesc::UINT16)
|
||||
}
|
||||
if (format == TypeDesc::UINT16) {
|
||||
return make_numpy_array((unsigned short*)data, dims, chans, width,
|
||||
height, depth);
|
||||
if (format == TypeDesc::INT8)
|
||||
}
|
||||
if (format == TypeDesc::INT8) {
|
||||
return make_numpy_array((char*)data, dims, chans, width, height, depth);
|
||||
if (format == TypeDesc::INT16)
|
||||
}
|
||||
if (format == TypeDesc::INT16) {
|
||||
return make_numpy_array((short*)data, dims, chans, width, height,
|
||||
depth);
|
||||
if (format == TypeDesc::DOUBLE)
|
||||
}
|
||||
if (format == TypeDesc::DOUBLE) {
|
||||
return make_numpy_array((double*)data, dims, chans, width, height,
|
||||
depth);
|
||||
if (format == TypeDesc::HALF)
|
||||
}
|
||||
if (format == TypeDesc::HALF) {
|
||||
return make_numpy_array((half*)data, dims, chans, width, height, depth);
|
||||
if (format == TypeDesc::UINT)
|
||||
}
|
||||
if (format == TypeDesc::UINT) {
|
||||
return make_numpy_array((unsigned int*)data, dims, chans, width, height,
|
||||
depth);
|
||||
if (format == TypeDesc::INT)
|
||||
}
|
||||
if (format == TypeDesc::INT) {
|
||||
return make_numpy_array((int*)data, dims, chans, width, height, depth);
|
||||
}
|
||||
delete[] (char*)data;
|
||||
return py::none();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
|
||||
@@ -631,19 +736,19 @@ template<typename C>
|
||||
inline void
|
||||
delegate_setitem(C& self, const std::string& key, py::object obj)
|
||||
{
|
||||
if (py::isinstance<py::float_>(obj))
|
||||
self[key] = float(obj.template cast<py::float_>());
|
||||
else if (py::isinstance<py::int_>(obj))
|
||||
self[key] = int(obj.template cast<py::int_>());
|
||||
else if (py::isinstance<py::str>(obj))
|
||||
self[key] = std::string(obj.template cast<py::str>());
|
||||
else if (py::isinstance<py::bytes>(obj))
|
||||
self[key] = std::string(obj.template cast<py::bytes>());
|
||||
else
|
||||
if (py::isinstance<py::float_>(obj)) {
|
||||
self[key] = py::cast<float>(obj);
|
||||
} else if (py::isinstance<py::int_>(obj)) {
|
||||
self[key] = py::cast<int>(obj);
|
||||
} else if (py::isinstance<py::str>(obj)) {
|
||||
self[key] = oiio_py::str_to_stdstring(obj);
|
||||
} else if (py::isinstance<py::bytes>(obj)) {
|
||||
self[key] = oiio_py::bytes_to_stdstring(py::cast<py::bytes>(obj));
|
||||
} else {
|
||||
throw std::invalid_argument("Bad type for __setitem__");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
} // namespace PyOpenImageIO
|
||||
|
||||
#endif // PYOPENIMAGEIO_PY_OIIO_H
|
||||
|
||||
@@ -15,8 +15,9 @@ ParamValue_from_pyobject(string_view name, TypeDesc type, int nvalues,
|
||||
// Unsized uint8[] + bytes infers arraylen — must run before numelements().
|
||||
if (type.basetype == TypeDesc::UINT8 && type.arraylen
|
||||
&& py::isinstance<py::bytes>(obj)) {
|
||||
TypeDesc t = type;
|
||||
std::string s = obj.cast<py::bytes>();
|
||||
TypeDesc t = type;
|
||||
py::bytes b = py::cast<py::bytes>(obj);
|
||||
const std::string s = oiio_py::bytes_to_stdstring(b);
|
||||
if (type.arraylen < 0)
|
||||
t.arraylen = int(s.size()) / nvalues;
|
||||
if (t.arraylen * nvalues == int(s.size())) {
|
||||
@@ -140,10 +141,8 @@ attribute_typed(T& myobj, string_view name, TypeDesc type, int nvalues,
|
||||
|
||||
|
||||
void
|
||||
declare_paramvalue(py::module& m)
|
||||
declare_paramvalue(py_module& m)
|
||||
{
|
||||
using namespace pybind11::literals;
|
||||
|
||||
py::enum_<ParamValue::Interp>(m, "Interp")
|
||||
.value("CONSTANT", ParamValue::INTERP_CONSTANT)
|
||||
.value("PERPIECE", ParamValue::INTERP_PERPIECE)
|
||||
@@ -155,25 +154,42 @@ declare_paramvalue(py::module& m)
|
||||
.value("INTERP_LINEAR", ParamValue::INTERP_LINEAR)
|
||||
.value("INTERP_VERTEX", ParamValue::INTERP_VERTEX);
|
||||
|
||||
|
||||
py::class_<ParamValue>(m, "ParamValue")
|
||||
.def_property_readonly("name",
|
||||
[](const ParamValue& self) {
|
||||
return PY_STR(self.name().string());
|
||||
})
|
||||
.def_property_readonly("type",
|
||||
[](const ParamValue& self) {
|
||||
return self.type();
|
||||
})
|
||||
.def_property_readonly("value",
|
||||
[](const ParamValue& self) {
|
||||
return make_pyobject(self.data(),
|
||||
self.type(),
|
||||
self.nvalues());
|
||||
})
|
||||
.def_property_readonly("__len__", &ParamValue::nvalues)
|
||||
.OIIO_PY_PROP_RO("name",
|
||||
[](const ParamValue& self) {
|
||||
return oiio_py::str(self.name().string());
|
||||
})
|
||||
.OIIO_PY_PROP_RO("type",
|
||||
[](const ParamValue& self) { return self.type(); })
|
||||
.OIIO_PY_PROP_RO("value",
|
||||
[](const ParamValue& self) {
|
||||
return make_pyobject(self.data(), self.type(),
|
||||
self.nvalues());
|
||||
})
|
||||
.OIIO_PY_PROP_RO("__len__", &ParamValue::nvalues)
|
||||
.def(py::init<const std::string&, int>())
|
||||
.def(py::init<const std::string&, float>())
|
||||
.def(py::init<const std::string&, const std::string&>())
|
||||
#if defined(OIIO_PY_BACKEND_NANOBIND)
|
||||
.def(
|
||||
"__init__",
|
||||
[](ParamValue* self, const std::string& name, TypeDesc type,
|
||||
const py::object& obj) {
|
||||
new (self) ParamValue(
|
||||
ParamValue_from_pyobject(name, type, 1,
|
||||
ParamValue::INTERP_CONSTANT, obj));
|
||||
},
|
||||
"name"_a, "type"_a, "value"_a)
|
||||
.def(
|
||||
"__init__",
|
||||
[](ParamValue* self, const std::string& name, TypeDesc type,
|
||||
int nvalues, ParamValue::Interp interp, const py::object& obj) {
|
||||
new (self) ParamValue(
|
||||
ParamValue_from_pyobject(name, type, nvalues, interp, obj));
|
||||
},
|
||||
"name"_a, "type"_a, "nvalues"_a, "interp"_a, "value"_a);
|
||||
#else
|
||||
.def(py::init([](const std::string& name, TypeDesc type,
|
||||
const py::object& obj) {
|
||||
return ParamValue_from_pyobject(name, type, 1,
|
||||
@@ -187,6 +203,8 @@ declare_paramvalue(py::module& m)
|
||||
obj);
|
||||
}),
|
||||
"name"_a, "type"_a, "nvalues"_a, "interp"_a, "value"_a);
|
||||
#endif
|
||||
|
||||
|
||||
py::class_<ParamValueList>(m, "ParamValueList")
|
||||
.def(py::init<>())
|
||||
@@ -197,17 +215,18 @@ declare_paramvalue(py::module& m)
|
||||
throw py::index_error();
|
||||
return self[i];
|
||||
},
|
||||
py::return_value_policy::reference_internal)
|
||||
oiio_py::ref_internal)
|
||||
// __getitem__ is the dict-like `pvl[key]` lookup
|
||||
.def(
|
||||
"__getitem__",
|
||||
[](const ParamValueList& self, const std::string& key) {
|
||||
auto p = self.find(key);
|
||||
if (p == self.end())
|
||||
throw py::key_error("key '" + key + "' does not exist");
|
||||
oiio_py::throw_key_error("key '" + key
|
||||
+ "' does not exist");
|
||||
return make_pyobject(p->data(), p->type());
|
||||
},
|
||||
py::return_value_policy::reference_internal)
|
||||
oiio_py::ref_internal)
|
||||
// __setitem__ is the dict-like `pvl[key] = value` assignment
|
||||
.def("__setitem__",
|
||||
[](ParamValueList& self, const std::string& key, py::object val) {
|
||||
@@ -225,7 +244,7 @@ declare_paramvalue(py::module& m)
|
||||
.def(
|
||||
"__iter__",
|
||||
[](const ParamValueList& self) {
|
||||
return py::make_iterator(self.begin(), self.end());
|
||||
return oiio_py::make_iterator(self);
|
||||
},
|
||||
py::keep_alive<0, 1>())
|
||||
.def("append", [](ParamValueList& p,
|
||||
|
||||
+18
-22
@@ -7,9 +7,6 @@
|
||||
namespace PyOpenImageIO {
|
||||
|
||||
|
||||
static ROI ROI_All;
|
||||
|
||||
|
||||
static bool
|
||||
roi_contains_coord(const ROI& roi, int x, int y, int z, int ch)
|
||||
{
|
||||
@@ -27,19 +24,17 @@ roi_contains_roi(const ROI& roi, const ROI& other)
|
||||
|
||||
// Declare the OIIO ROI class to Python
|
||||
void
|
||||
declare_roi(py::module& m)
|
||||
declare_roi(py_module& m)
|
||||
{
|
||||
using namespace py;
|
||||
|
||||
py::class_<ROI>(m, "ROI")
|
||||
.def_readwrite("xbegin", &ROI::xbegin)
|
||||
.def_readwrite("xend", &ROI::xend)
|
||||
.def_readwrite("ybegin", &ROI::ybegin)
|
||||
.def_readwrite("yend", &ROI::yend)
|
||||
.def_readwrite("zbegin", &ROI::zbegin)
|
||||
.def_readwrite("zend", &ROI::zend)
|
||||
.def_readwrite("chbegin", &ROI::chbegin)
|
||||
.def_readwrite("chend", &ROI::chend)
|
||||
.OIIO_PY_RW("xbegin", &ROI::xbegin)
|
||||
.OIIO_PY_RW("xend", &ROI::xend)
|
||||
.OIIO_PY_RW("ybegin", &ROI::ybegin)
|
||||
.OIIO_PY_RW("yend", &ROI::yend)
|
||||
.OIIO_PY_RW("zbegin", &ROI::zbegin)
|
||||
.OIIO_PY_RW("zend", &ROI::zend)
|
||||
.OIIO_PY_RW("chbegin", &ROI::chbegin)
|
||||
.OIIO_PY_RW("chend", &ROI::chend)
|
||||
|
||||
.def(py::init<>())
|
||||
.def(py::init<int, int, int, int>())
|
||||
@@ -48,22 +43,22 @@ declare_roi(py::module& m)
|
||||
.def(py::init<const ROI&>())
|
||||
|
||||
// .def("defined", [](const ROI& roi) { return (int)roi.defined(); })
|
||||
.def_property_readonly("defined", &ROI::defined)
|
||||
.def_property_readonly("width", &ROI::width)
|
||||
.def_property_readonly("height", &ROI::height)
|
||||
.def_property_readonly("depth", &ROI::depth)
|
||||
.def_property_readonly("nchannels", &ROI::nchannels)
|
||||
.def_property_readonly("npixels", &ROI::npixels)
|
||||
.OIIO_PY_PROP_RO("defined", &ROI::defined)
|
||||
.OIIO_PY_PROP_RO("width", &ROI::width)
|
||||
.OIIO_PY_PROP_RO("height", &ROI::height)
|
||||
.OIIO_PY_PROP_RO("depth", &ROI::depth)
|
||||
.OIIO_PY_PROP_RO("nchannels", &ROI::nchannels)
|
||||
.OIIO_PY_PROP_RO("npixels", &ROI::npixels)
|
||||
.def("contains", &roi_contains_coord, "x"_a, "y"_a, "z"_a = 0,
|
||||
"ch"_a = 0)
|
||||
.def("contains", &roi_contains_roi, "other"_a)
|
||||
|
||||
.def_readonly_static("All", &ROI_All)
|
||||
.OIIO_PY_RO_STATIC("All", [](const py::object&) { return ROI::All(); })
|
||||
|
||||
// Conversion to string
|
||||
.def("__str__",
|
||||
[](const ROI& roi) {
|
||||
return PY_STR(Strutil::fmt::format("{}", roi));
|
||||
return oiio_py::str(Strutil::fmt::format("{}", roi));
|
||||
})
|
||||
|
||||
// Copy
|
||||
@@ -77,6 +72,7 @@ declare_roi(py::module& m)
|
||||
.def(py::self != py::self) // operator!= // NOSONAR
|
||||
;
|
||||
|
||||
|
||||
m.def("union", &roi_union);
|
||||
m.def("intersection", &roi_intersection);
|
||||
m.def("get_roi", &get_roi);
|
||||
|
||||
+89
-13
@@ -10,7 +10,7 @@ namespace PyOpenImageIO {
|
||||
|
||||
// Declare the OIIO TypeDesc type to Python
|
||||
void
|
||||
declare_typedesc(py::module& m)
|
||||
declare_typedesc(py_module& m)
|
||||
{
|
||||
py::enum_<TypeDesc::BASETYPE>(m, "BASETYPE")
|
||||
.value("UNKNOWN", TypeDesc::UNKNOWN)
|
||||
@@ -61,27 +61,28 @@ declare_typedesc(py::module& m)
|
||||
.value("BOX", TypeDesc::BOX)
|
||||
.export_values();
|
||||
|
||||
|
||||
py::class_<TypeDesc>(m, "TypeDesc")
|
||||
// basetype, aggregate, and vecsemantics should look like BASETYPE,
|
||||
// AGGREGATE, VECSEMANTICS, but since they are stored as unsigned
|
||||
// char, def_readwrite() doesn't do the right thing. Instead, we
|
||||
// use set_foo/get_foo wrappers, but from Python it looks like
|
||||
// regular member access.
|
||||
.def_property(
|
||||
.OIIO_PY_PROP_RW(
|
||||
"basetype",
|
||||
[](TypeDesc t) { return TypeDesc::BASETYPE(t.basetype); },
|
||||
[](TypeDesc& t, TypeDesc::BASETYPE b) { return t.basetype = b; })
|
||||
.def_property(
|
||||
.OIIO_PY_PROP_RW(
|
||||
"aggregate",
|
||||
[](TypeDesc t) { return TypeDesc::AGGREGATE(t.aggregate); },
|
||||
[](TypeDesc& t, TypeDesc::AGGREGATE b) { return t.aggregate = b; })
|
||||
.def_property(
|
||||
.OIIO_PY_PROP_RW(
|
||||
"vecsemantics",
|
||||
[](TypeDesc t) { return TypeDesc::VECSEMANTICS(t.vecsemantics); },
|
||||
[](TypeDesc& t, TypeDesc::VECSEMANTICS b) {
|
||||
return t.vecsemantics = b;
|
||||
})
|
||||
.def_readwrite("arraylen", &TypeDesc::arraylen)
|
||||
.OIIO_PY_RW("arraylen", &TypeDesc::arraylen)
|
||||
// Constructors: () [defined implicitly], (base), (base, agg),
|
||||
// (base,agg,vecsem), (base,agg,vecsem,arraylen), string.
|
||||
.def(py::init<>())
|
||||
@@ -101,7 +102,8 @@ declare_typedesc(py::module& m)
|
||||
// .def(init<TypeDesc::BASETYPE, int>())
|
||||
// .def(init<TypeDesc::BASETYPE, TypeDesc::AGGREGATE, int>())
|
||||
// FIXME -- I bet this works with Pybind11
|
||||
.def("c_str", [](const TypeDesc& self) { return PY_STR(self.c_str()); })
|
||||
.def("c_str",
|
||||
[](const TypeDesc& self) { return oiio_py::str(self.c_str()); })
|
||||
.def("numelements", &TypeDesc::numelements)
|
||||
.def("basevalues", &TypeDesc::basevalues)
|
||||
.def("size", &TypeDesc::size)
|
||||
@@ -114,11 +116,36 @@ declare_typedesc(py::module& m)
|
||||
})
|
||||
.def("equivalent", &TypeDesc::equivalent)
|
||||
.def("unarray", &TypeDesc::unarray)
|
||||
.def("is_vec2", &TypeDesc::is_vec2)
|
||||
.def("is_vec3", &TypeDesc::is_vec3)
|
||||
.def("is_vec4", &TypeDesc::is_vec4)
|
||||
.def("is_box2", &TypeDesc::is_box2)
|
||||
.def("is_box3", &TypeDesc::is_box3)
|
||||
.def(
|
||||
"is_vec2",
|
||||
[](const TypeDesc& t, TypeDesc::BASETYPE b = TypeDesc::FLOAT) {
|
||||
return t.is_vec2(b);
|
||||
},
|
||||
"b"_a = TypeDesc::FLOAT)
|
||||
.def(
|
||||
"is_vec3",
|
||||
[](const TypeDesc& t, TypeDesc::BASETYPE b = TypeDesc::FLOAT) {
|
||||
return t.is_vec3(b);
|
||||
},
|
||||
"b"_a = TypeDesc::FLOAT)
|
||||
.def(
|
||||
"is_vec4",
|
||||
[](const TypeDesc& t, TypeDesc::BASETYPE b = TypeDesc::FLOAT) {
|
||||
return t.is_vec4(b);
|
||||
},
|
||||
"b"_a = TypeDesc::FLOAT)
|
||||
.def(
|
||||
"is_box2",
|
||||
[](const TypeDesc& t, TypeDesc::BASETYPE b = TypeDesc::FLOAT) {
|
||||
return t.is_box2(b);
|
||||
},
|
||||
"b"_a = TypeDesc::FLOAT)
|
||||
.def(
|
||||
"is_box3",
|
||||
[](const TypeDesc& t, TypeDesc::BASETYPE b = TypeDesc::FLOAT) {
|
||||
return t.is_box3(b);
|
||||
},
|
||||
"b"_a = TypeDesc::FLOAT)
|
||||
.def_static("all_types_equal",
|
||||
[](const std::vector<TypeDesc>& types) {
|
||||
return TypeDesc::all_types_equal(types);
|
||||
@@ -129,11 +156,12 @@ declare_typedesc(py::module& m)
|
||||
.def(py::self != py::self) // operator!= //NOSONAR
|
||||
|
||||
// Conversion to string
|
||||
.def("__str__", [](TypeDesc t) { return PY_STR(t.c_str()); })
|
||||
.def("__str__", [](TypeDesc t) { return oiio_py::str(t.c_str()); })
|
||||
.def("__repr__", [](TypeDesc t) {
|
||||
return PY_STR("<TypeDesc '" + std::string(t.c_str()) + "'>");
|
||||
return oiio_py::str("<TypeDesc '" + std::string(t.c_str()) + "'>");
|
||||
});
|
||||
|
||||
|
||||
// Declare that a BASETYPE is implicitly convertible to a TypeDesc.
|
||||
// This keeps us from having to separately declare func(TypeDesc)
|
||||
// and func(TypeDesc::BASETYPE) everywhere.
|
||||
@@ -144,6 +172,54 @@ declare_typedesc(py::module& m)
|
||||
// foo(TypeUInt8).
|
||||
py::implicitly_convertible<py::str, TypeDesc>();
|
||||
|
||||
#if defined(OIIO_PY_BACKEND_NANOBIND)
|
||||
// Pybind11's .export_values() above copies enum members onto the module
|
||||
// (oiio.FLOAT, oiio.VEC3, ...). Nanobind has no equivalent here, so set
|
||||
// them explicitly to keep the same public API.
|
||||
m.attr("UNKNOWN") = py::cast(TypeDesc::UNKNOWN);
|
||||
m.attr("NONE") = py::cast(TypeDesc::NONE);
|
||||
m.attr("UCHAR") = py::cast(TypeDesc::UCHAR);
|
||||
m.attr("UINT8") = py::cast(TypeDesc::UINT8);
|
||||
m.attr("CHAR") = py::cast(TypeDesc::CHAR);
|
||||
m.attr("INT8") = py::cast(TypeDesc::INT8);
|
||||
m.attr("UINT16") = py::cast(TypeDesc::UINT16);
|
||||
m.attr("USHORT") = py::cast(TypeDesc::USHORT);
|
||||
m.attr("SHORT") = py::cast(TypeDesc::SHORT);
|
||||
m.attr("INT16") = py::cast(TypeDesc::INT16);
|
||||
m.attr("UINT") = py::cast(TypeDesc::UINT);
|
||||
m.attr("UINT32") = py::cast(TypeDesc::UINT32);
|
||||
m.attr("INT") = py::cast(TypeDesc::INT);
|
||||
m.attr("INT32") = py::cast(TypeDesc::INT32);
|
||||
m.attr("ULONGLONG") = py::cast(TypeDesc::ULONGLONG);
|
||||
m.attr("UINT64") = py::cast(TypeDesc::UINT64);
|
||||
m.attr("LONGLONG") = py::cast(TypeDesc::LONGLONG);
|
||||
m.attr("INT64") = py::cast(TypeDesc::INT64);
|
||||
m.attr("HALF") = py::cast(TypeDesc::HALF);
|
||||
m.attr("FLOAT") = py::cast(TypeDesc::FLOAT);
|
||||
m.attr("DOUBLE") = py::cast(TypeDesc::DOUBLE);
|
||||
m.attr("STRING") = py::cast(TypeDesc::STRING);
|
||||
m.attr("PTR") = py::cast(TypeDesc::PTR);
|
||||
m.attr("LASTBASE") = py::cast(TypeDesc::LASTBASE);
|
||||
|
||||
m.attr("SCALAR") = py::cast(TypeDesc::SCALAR);
|
||||
m.attr("VEC2") = py::cast(TypeDesc::VEC2);
|
||||
m.attr("VEC3") = py::cast(TypeDesc::VEC3);
|
||||
m.attr("VEC4") = py::cast(TypeDesc::VEC4);
|
||||
m.attr("MATRIX33") = py::cast(TypeDesc::MATRIX33);
|
||||
m.attr("MATRIX44") = py::cast(TypeDesc::MATRIX44);
|
||||
|
||||
m.attr("NOXFORM") = py::cast(TypeDesc::NOXFORM);
|
||||
m.attr("NOSEMANTICS") = py::cast(TypeDesc::NOSEMANTICS);
|
||||
m.attr("COLOR") = py::cast(TypeDesc::COLOR);
|
||||
m.attr("POINT") = py::cast(TypeDesc::POINT);
|
||||
m.attr("VECTOR") = py::cast(TypeDesc::VECTOR);
|
||||
m.attr("NORMAL") = py::cast(TypeDesc::NORMAL);
|
||||
m.attr("TIMECODE") = py::cast(TypeDesc::TIMECODE);
|
||||
m.attr("KEYCODE") = py::cast(TypeDesc::KEYCODE);
|
||||
m.attr("RATIONAL") = py::cast(TypeDesc::RATIONAL);
|
||||
m.attr("BOX") = py::cast(TypeDesc::BOX);
|
||||
#endif
|
||||
|
||||
// Global constants of common TypeDescs
|
||||
m.attr("TypeUnknown") = TypeUnknown;
|
||||
m.attr("TypeFloat") = TypeFloat;
|
||||
|
||||
@@ -93,6 +93,13 @@ getattribute('foo_no') retrieves None
|
||||
getattribute('smpte:TimeCode') retrieves (18356486, 4294967295)
|
||||
getattribute('ucarr') retrieves [49 50 51 0 0 97 98 99 1 88]
|
||||
getattribute('unknown') retrieves None
|
||||
|
||||
Conversion helper roundtrips (py_to_stdvector, C_to_tuple):
|
||||
Passed: C_to_tuple vector getattribute
|
||||
Passed: py_buffer_to_stdvector float numpy buffer
|
||||
Passed: buffer_format int buffer to float[2]
|
||||
Passed: py_indexable list to float vector
|
||||
Passed: py_indexable tuple to float vector
|
||||
s.get('foo_int') = 14
|
||||
s.get('ucarr') retrieves [49 50 51 0 0 97 98 99 1 88]
|
||||
s['ucarr'] retrieves [49 50 51 0 0 97 98 99 1 88]
|
||||
|
||||
@@ -128,6 +128,42 @@ try:
|
||||
print ("getattribute('smpte:TimeCode') retrieves", s.getattribute("smpte:TimeCode"))
|
||||
print ("getattribute('ucarr') retrieves", s.getattribute("ucarr"))
|
||||
print ("getattribute('unknown') retrieves", s.getattribute("unknown"))
|
||||
print ()
|
||||
print ("Conversion helper roundtrips (py_to_stdvector, C_to_tuple):")
|
||||
s_conv = oiio.ImageSpec()
|
||||
s_conv.attribute("foo_vector", oiio.TypeVector, (1.0, 0.0, 11.0))
|
||||
vec = s_conv.getattribute("foo_vector")
|
||||
if tuple(vec) == (1.0, 0.0, 11.0):
|
||||
print ("Passed: C_to_tuple vector getattribute")
|
||||
else:
|
||||
print ("Failed: C_to_tuple vector getattribute expected (1.0, 0.0, 11.0) got", vec)
|
||||
s_conv.attribute("float_buf_probe", oiio.TypeDesc("float[4]"),
|
||||
numpy.array([1.0, 2.0, 3.0, 4.0], dtype="f"))
|
||||
fb = s_conv.getattribute("float_buf_probe")
|
||||
if fb is not None and tuple(fb) == (1.0, 2.0, 3.0, 4.0):
|
||||
print ("Passed: py_buffer_to_stdvector float numpy buffer")
|
||||
else:
|
||||
print ("Failed: py_buffer_to_stdvector float numpy buffer got", fb)
|
||||
s_conv.attribute("float_from_int_probe", oiio.TypeDesc("float[2]"),
|
||||
numpy.array([3, 4], dtype="i"))
|
||||
fi = s_conv.getattribute("float_from_int_probe")
|
||||
if fi is not None and tuple(fi) == (3.0, 4.0):
|
||||
print ("Passed: buffer_format int buffer to float[2]")
|
||||
else:
|
||||
print ("Failed: buffer_format int buffer to float[2] got", fi)
|
||||
s_conv.attribute("list_float_probe", oiio.TypeDesc("float[4]"),
|
||||
[1.0, 2.0, 3.0, 4.0])
|
||||
lf = s_conv.getattribute("list_float_probe")
|
||||
if lf is not None and tuple(lf) == (1.0, 2.0, 3.0, 4.0):
|
||||
print ("Passed: py_indexable list to float vector")
|
||||
else:
|
||||
print ("Failed: py_indexable list to float vector got", lf)
|
||||
s_conv.attribute("tuple_float_probe", oiio.TypeDesc("float[2]"), (5.0, 6.0))
|
||||
tf = s_conv.getattribute("tuple_float_probe")
|
||||
if tf is not None and tuple(tf) == (5.0, 6.0):
|
||||
print ("Passed: py_indexable tuple to float vector")
|
||||
else:
|
||||
print ("Failed: py_indexable tuple to float vector got", tf)
|
||||
print ("s.get('foo_int') =", s.get('foo_int'))
|
||||
print ("s.get('ucarr') retrieves", s.get("ucarr"))
|
||||
try :
|
||||
|
||||
@@ -11,6 +11,9 @@ Testing individual ParamValue:
|
||||
item u8unsized uint8[10] [49 50 51 52 53 54 55 56 57 48]
|
||||
item u8fix_n2 uint8[2] [ 97 98 99 100 101 102]
|
||||
item u8var_n2 uint8[4] [49 50 51 52 53 54 55 56]
|
||||
ParamValue conversion helper checks:
|
||||
Passed: ParamValue tuple to float[4]
|
||||
Passed: ParamValue numpy float buffer to color
|
||||
|
||||
Testing ParamValueList:
|
||||
pl length is 9
|
||||
|
||||
@@ -35,8 +35,8 @@ try:
|
||||
pv = oiio.ParamValue("c", "xyzpdq")
|
||||
print_param_value(pv)
|
||||
# Construct from tuple
|
||||
pv = oiio.ParamValue("d", "float[4]", (3.5, 4.5, 5.5, 6.5))
|
||||
print_param_value(pv)
|
||||
pv_tuple = oiio.ParamValue("d", "float[4]", (3.5, 4.5, 5.5, 6.5))
|
||||
print_param_value(pv_tuple)
|
||||
# Construct from tuple with nvalues/interp
|
||||
pv = oiio.ParamValue("e", "float", 4, oiio.Interp.LINEAR, (1, 3, 5, 7))
|
||||
print_param_value(pv)
|
||||
@@ -44,9 +44,9 @@ try:
|
||||
pv = oiio.ParamValue("f", "point", [3.5, 4.5, 5.5, 6.5])
|
||||
print_param_value(pv)
|
||||
# Construct from a numpy array
|
||||
pv = oiio.ParamValue("g", "color",
|
||||
numpy.array([0.25, 0.5, 0.75], dtype='f'))
|
||||
print_param_value(pv)
|
||||
pv_np = oiio.ParamValue("g", "color",
|
||||
numpy.array([0.25, 0.5, 0.75], dtype='f'))
|
||||
print_param_value(pv_np)
|
||||
# Construct from numpy byte array
|
||||
pv = oiio.ParamValue("ucarr", "uint8[10]", numpy.array([49, 50, 51, 0, 0, 97, 98, 99, 1, 88], dtype='B'))
|
||||
print_param_value(pv)
|
||||
@@ -87,6 +87,16 @@ try:
|
||||
# Check: type uint8[4] with 2 nvalues; exercises inference with nvalues>1.
|
||||
print_param_value(pv)
|
||||
|
||||
print ("ParamValue conversion helper checks:")
|
||||
if pv_tuple.value == (3.5, 4.5, 5.5, 6.5):
|
||||
print ("Passed: ParamValue tuple to float[4]")
|
||||
else:
|
||||
print ("Failed: ParamValue tuple to float[4] got", pv_tuple.value)
|
||||
if pv_np.value is not None and tuple(pv_np.value) == (0.25, 0.5, 0.75):
|
||||
print ("Passed: ParamValue numpy float buffer to color")
|
||||
else:
|
||||
print ("Failed: ParamValue numpy float buffer to color got", pv_np.value)
|
||||
|
||||
print ("")
|
||||
|
||||
print ("Testing ParamValueList:")
|
||||
|
||||
@@ -177,6 +177,11 @@ type 'fromstring('point')'
|
||||
after unarray('float[2]') = float
|
||||
vector is_vec2,is_vec3,is_vec4 = False True False
|
||||
box2i is_box2,is_box3 = True False
|
||||
vector is_vec2(),is_vec3(),is_vec4() defaults = False True False
|
||||
vector defaults match explicit FLOAT = True True True
|
||||
box2 is_box2(),is_box3() defaults = True False
|
||||
box2 defaults match explicit FLOAT = True True
|
||||
box2i is_box2() default vs INT = False True
|
||||
all_types_equal([uint8,uint8]) = True
|
||||
all_types_equal([uint8,uint16]) = False
|
||||
repr(TypeFloat) = <TypeDesc 'float'>
|
||||
|
||||
@@ -164,6 +164,22 @@ try:
|
||||
print ("box2i is_box2,is_box3 =",
|
||||
oiio.TypeDesc("box2i").is_box2(oiio.INT),
|
||||
oiio.TypeDesc("box2i").is_box3(oiio.INT))
|
||||
vector = oiio.TypeDesc("vector")
|
||||
box2 = oiio.TypeDesc("box2")
|
||||
box2i = oiio.TypeDesc("box2i")
|
||||
print ("vector is_vec2(),is_vec3(),is_vec4() defaults =",
|
||||
vector.is_vec2(), vector.is_vec3(), vector.is_vec4())
|
||||
print ("vector defaults match explicit FLOAT =",
|
||||
vector.is_vec2() == vector.is_vec2(oiio.FLOAT),
|
||||
vector.is_vec3() == vector.is_vec3(oiio.FLOAT),
|
||||
vector.is_vec4() == vector.is_vec4(oiio.FLOAT))
|
||||
print ("box2 is_box2(),is_box3() defaults =",
|
||||
box2.is_box2(), box2.is_box3())
|
||||
print ("box2 defaults match explicit FLOAT =",
|
||||
box2.is_box2() == box2.is_box2(oiio.FLOAT),
|
||||
box2.is_box3() == box2.is_box3(oiio.FLOAT))
|
||||
print ("box2i is_box2() default vs INT =",
|
||||
box2i.is_box2(), box2i.is_box2(oiio.INT))
|
||||
print ("all_types_equal([uint8,uint8]) =",
|
||||
oiio.TypeDesc.all_types_equal([oiio.TypeDesc("uint8"),
|
||||
oiio.TypeDesc("uint8")]))
|
||||
|
||||
Reference in New Issue
Block a user