Browse Source

python: expose compressed image APIs in AbstractImageConverter.

next
Vladimír Vondruš 3 years ago
parent
commit
85e8fae9b8
  1. 60
      doc/python/magnum.trade.rst
  2. 3
      package/ci/appveyor-desktop-gles.bat
  3. 3
      package/ci/appveyor-desktop.bat
  4. 3
      package/ci/unix-desktop-gles.sh
  5. 3
      package/ci/unix-desktop.sh
  6. 68
      src/python/magnum/test/test_trade.py
  7. 18
      src/python/magnum/trade.cpp

60
doc/python/magnum.trade.rst

@ -595,12 +595,48 @@
raising an exception. See particular function documentation for detailed raising an exception. See particular function documentation for detailed
behavior. behavior.
.. py:function:: magnum.trade.AbstractImageConverter.convert(self, image: magnum.trade.ImageData1D)
:raise RuntimeError: If image conversion fails
.. py:function:: magnum.trade.AbstractImageConverter.convert(self, image: magnum.trade.ImageData2D)
:raise RuntimeError: If image conversion fails
.. py:function:: magnum.trade.AbstractImageConverter.convert(self, image: magnum.trade.ImageData3D)
:raise RuntimeError: If image conversion fails
.. py:function:: magnum.trade.AbstractImageConverter.convert(self, image: magnum.ImageView1D) .. py:function:: magnum.trade.AbstractImageConverter.convert(self, image: magnum.ImageView1D)
:raise RuntimeError: If image conversion fails :raise RuntimeError: If image conversion fails
.. py:function:: magnum.trade.AbstractImageConverter.convert(self, image: magnum.ImageView2D) .. py:function:: magnum.trade.AbstractImageConverter.convert(self, image: magnum.ImageView2D)
:raise RuntimeError: If image conversion fails :raise RuntimeError: If image conversion fails
.. py:function:: magnum.trade.AbstractImageConverter.convert(self, image: magnum.ImageView3D) .. py:function:: magnum.trade.AbstractImageConverter.convert(self, image: magnum.ImageView3D)
:raise RuntimeError: If image conversion fails :raise RuntimeError: If image conversion fails
.. py:function:: magnum.trade.AbstractImageConverter.convert(self, image: magnum.CompressedImageView1D)
:raise RuntimeError: If image conversion fails
.. py:function:: magnum.trade.AbstractImageConverter.convert(self, image: magnum.CompressedImageView2D)
:raise RuntimeError: If image conversion fails
.. py:function:: magnum.trade.AbstractImageConverter.convert(self, image: magnum.CompressedImageView3D)
:raise RuntimeError: If image conversion fails
.. py:function:: magnum.trade.AbstractImageConverter.convert_to_file(self, image: magnum.trade.ImageData1D, filename: str)
:raise RuntimeError: If image conversion fails
For compatibility with :ref:`os.path`, on Windows this function converts
all backslashes in :p:`filename` to forward slashes before passing it to
:dox:`Trade::AbstractImageConverter::convertToFile()`, which expects
forward slashes as directory separators on all platforms.
.. py:function:: magnum.trade.AbstractImageConverter.convert_to_file(self, image: magnum.trade.ImageData2D, filename: str)
:raise RuntimeError: If image conversion fails
For compatibility with :ref:`os.path`, on Windows this function converts
all backslashes in :p:`filename` to forward slashes before passing it to
:dox:`Trade::AbstractImageConverter::convertToFile()`, which expects
forward slashes as directory separators on all platforms.
.. py:function:: magnum.trade.AbstractImageConverter.convert_to_file(self, image: magnum.trade.ImageData3D, filename: str)
:raise RuntimeError: If image conversion fails
For compatibility with :ref:`os.path`, on Windows this function converts
all backslashes in :p:`filename` to forward slashes before passing it to
:dox:`Trade::AbstractImageConverter::convertToFile()`, which expects
forward slashes as directory separators on all platforms.
.. py:function:: magnum.trade.AbstractImageConverter.convert_to_file(self, image: magnum.ImageView1D, filename: str) .. py:function:: magnum.trade.AbstractImageConverter.convert_to_file(self, image: magnum.ImageView1D, filename: str)
:raise RuntimeError: If image conversion fails :raise RuntimeError: If image conversion fails
@ -626,6 +662,30 @@
:dox:`Trade::AbstractImageConverter::convertToFile()`, which expects :dox:`Trade::AbstractImageConverter::convertToFile()`, which expects
forward slashes as directory separators on all platforms. forward slashes as directory separators on all platforms.
.. py:function:: magnum.trade.AbstractImageConverter.convert_to_file(self, image: magnum.CompressedImageView1D, filename: str)
:raise RuntimeError: If image conversion fails
For compatibility with :ref:`os.path`, on Windows this function converts
all backslashes in :p:`filename` to forward slashes before passing it to
:dox:`Trade::AbstractImageConverter::convertToFile()`, which expects
forward slashes as directory separators on all platforms.
.. py:function:: magnum.trade.AbstractImageConverter.convert_to_file(self, image: magnum.CompressedImageView2D, filename: str)
:raise RuntimeError: If image conversion fails
For compatibility with :ref:`os.path`, on Windows this function converts
all backslashes in :p:`filename` to forward slashes before passing it to
:dox:`Trade::AbstractImageConverter::convertToFile()`, which expects
forward slashes as directory separators on all platforms.
.. py:function:: magnum.trade.AbstractImageConverter.convert_to_file(self, image: magnum.CompressedImageView3D, filename: str)
:raise RuntimeError: If image conversion fails
For compatibility with :ref:`os.path`, on Windows this function converts
all backslashes in :p:`filename` to forward slashes before passing it to
:dox:`Trade::AbstractImageConverter::convertToFile()`, which expects
forward slashes as directory separators on all platforms.
.. py:class:: magnum.trade.SceneConverterManager .. py:class:: magnum.trade.SceneConverterManager
:summary: Manager for :ref:`AbstractSceneConverter` plugin instances :summary: Manager for :ref:`AbstractSceneConverter` plugin instances

3
package/ci/appveyor-desktop-gles.bat

@ -78,9 +78,12 @@ cmake .. ^
-DCMAKE_BUILD_TYPE=Release ^ -DCMAKE_BUILD_TYPE=Release ^
-DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/deps ^ -DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/deps ^
-DMAGNUM_BUILD_STATIC=%BUILD_STATIC% ^ -DMAGNUM_BUILD_STATIC=%BUILD_STATIC% ^
-DMAGNUM_WITH_BCDECIMAGECONVERTER=ON ^
-DMAGNUM_WITH_DDSIMPORTER=ON ^ -DMAGNUM_WITH_DDSIMPORTER=ON ^
-DMAGNUM_WITH_ETCDECIMAGECONVERTER=ON ^
-DMAGNUM_WITH_GLTFIMPORTER=ON ^ -DMAGNUM_WITH_GLTFIMPORTER=ON ^
-DMAGNUM_WITH_GLTFSCENECONVERTER=ON ^ -DMAGNUM_WITH_GLTFSCENECONVERTER=ON ^
-DMAGNUM_WITH_KTXIMAGECONVERTER=ON ^
-DMAGNUM_WITH_MESHOPTIMIZERSCENECONVERTER=ON ^ -DMAGNUM_WITH_MESHOPTIMIZERSCENECONVERTER=ON ^
-DMAGNUM_WITH_PRIMITIVEIMPORTER=ON ^ -DMAGNUM_WITH_PRIMITIVEIMPORTER=ON ^
-DMAGNUM_WITH_STANFORDSCENECONVERTER=ON ^ -DMAGNUM_WITH_STANFORDSCENECONVERTER=ON ^

3
package/ci/appveyor-desktop.bat

@ -88,9 +88,12 @@ cmake .. ^
-DCMAKE_BUILD_TYPE=Release ^ -DCMAKE_BUILD_TYPE=Release ^
-DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/deps ^ -DCMAKE_INSTALL_PREFIX=%APPVEYOR_BUILD_FOLDER%/deps ^
-DMAGNUM_BUILD_STATIC=%BUILD_STATIC% ^ -DMAGNUM_BUILD_STATIC=%BUILD_STATIC% ^
-DMAGNUM_WITH_BCDECIMAGECONVERTER=ON ^
-DMAGNUM_WITH_DDSIMPORTER=ON ^ -DMAGNUM_WITH_DDSIMPORTER=ON ^
-DMAGNUM_WITH_ETCDECIMAGECONVERTER=ON ^
-DMAGNUM_WITH_GLTFIMPORTER=ON ^ -DMAGNUM_WITH_GLTFIMPORTER=ON ^
-DMAGNUM_WITH_GLTFSCENECONVERTER=ON ^ -DMAGNUM_WITH_GLTFSCENECONVERTER=ON ^
-DMAGNUM_WITH_KTXIMAGECONVERTER=ON ^
-DMAGNUM_WITH_MESHOPTIMIZERSCENECONVERTER=%EXCEPT_MSVC2017% ^ -DMAGNUM_WITH_MESHOPTIMIZERSCENECONVERTER=%EXCEPT_MSVC2017% ^
-DMAGNUM_WITH_PRIMITIVEIMPORTER=ON ^ -DMAGNUM_WITH_PRIMITIVEIMPORTER=ON ^
-DMAGNUM_WITH_STANFORDSCENECONVERTER=ON ^ -DMAGNUM_WITH_STANFORDSCENECONVERTER=ON ^

3
package/ci/unix-desktop-gles.sh

@ -65,9 +65,12 @@ cmake .. \
-DCMAKE_PREFIX_PATH=$HOME/swiftshader \ -DCMAKE_PREFIX_PATH=$HOME/swiftshader \
-DCMAKE_INSTALL_RPATH="$HOME/deps/lib;$HOME/swiftshader/lib" \ -DCMAKE_INSTALL_RPATH="$HOME/deps/lib;$HOME/swiftshader/lib" \
-DMAGNUM_BUILD_STATIC=$BUILD_STATIC \ -DMAGNUM_BUILD_STATIC=$BUILD_STATIC \
-DMAGNUM_WITH_BCDECIMAGECONVERTER=ON \
-DMAGNUM_WITH_DDSIMPORTER=ON \ -DMAGNUM_WITH_DDSIMPORTER=ON \
-DMAGNUM_WITH_ETCDECIMAGECONVERTER=ON \
-DMAGNUM_WITH_GLTFIMPORTER=ON \ -DMAGNUM_WITH_GLTFIMPORTER=ON \
-DMAGNUM_WITH_GLTFSCENECONVERTER=ON \ -DMAGNUM_WITH_GLTFSCENECONVERTER=ON \
-DMAGNUM_WITH_KTXIMAGECONVERTER=ON \
-DMAGNUM_WITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DMAGNUM_WITH_MESHOPTIMIZERSCENECONVERTER=ON \
-DMAGNUM_WITH_PRIMITIVEIMPORTER=ON \ -DMAGNUM_WITH_PRIMITIVEIMPORTER=ON \
-DMAGNUM_WITH_STANFORDSCENECONVERTER=ON \ -DMAGNUM_WITH_STANFORDSCENECONVERTER=ON \

3
package/ci/unix-desktop.sh

@ -69,9 +69,12 @@ cmake .. \
-DCMAKE_INSTALL_RPATH=$HOME/deps/lib \ -DCMAKE_INSTALL_RPATH=$HOME/deps/lib \
-DCMAKE_BUILD_TYPE=Release \ -DCMAKE_BUILD_TYPE=Release \
-DMAGNUM_BUILD_STATIC=$BUILD_STATIC \ -DMAGNUM_BUILD_STATIC=$BUILD_STATIC \
-DMAGNUM_WITH_BCDECIMAGECONVERTER=ON \
-DMAGNUM_WITH_DDSIMPORTER=ON \ -DMAGNUM_WITH_DDSIMPORTER=ON \
-DMAGNUM_WITH_ETCDECIMAGECONVERTER=ON \
-DMAGNUM_WITH_GLTFIMPORTER=ON \ -DMAGNUM_WITH_GLTFIMPORTER=ON \
-DMAGNUM_WITH_GLTFSCENECONVERTER=ON \ -DMAGNUM_WITH_GLTFSCENECONVERTER=ON \
-DMAGNUM_WITH_KTXIMAGECONVERTER=ON \
-DMAGNUM_WITH_MESHOPTIMIZERSCENECONVERTER=ON \ -DMAGNUM_WITH_MESHOPTIMIZERSCENECONVERTER=ON \
-DMAGNUM_WITH_PRIMITIVEIMPORTER=ON \ -DMAGNUM_WITH_PRIMITIVEIMPORTER=ON \
-DMAGNUM_WITH_STANFORDSCENECONVERTER=ON \ -DMAGNUM_WITH_STANFORDSCENECONVERTER=ON \

68
src/python/magnum/test/test_trade.py

@ -1664,8 +1664,11 @@ class ImageConverter(unittest.TestCase):
converter = trade.ImageConverterManager().load_and_instantiate('StbResizeImageConverter') converter = trade.ImageConverterManager().load_and_instantiate('StbResizeImageConverter')
converter.configuration['size'] = "1 1" converter.configuration['size'] = "1 1"
converted = converter.convert(image) # Both ImageView and ImageData should work
self.assertEqual(converted.size, Vector2i(1, 1)) converted1 = converter.convert(image)
converted2 = converter.convert(ImageView2D(image))
self.assertEqual(converted1.size, Vector2i(1, 1))
self.assertEqual(converted2.size, Vector2i(1, 1))
def test_image2d_failed(self): def test_image2d_failed(self):
importer = trade.ImporterManager().load_and_instantiate('StbImageImporter') importer = trade.ImporterManager().load_and_instantiate('StbImageImporter')
@ -1679,6 +1682,31 @@ class ImageConverter(unittest.TestCase):
with self.assertRaisesRegex(RuntimeError, "conversion failed"): with self.assertRaisesRegex(RuntimeError, "conversion failed"):
converter.convert(image) converter.convert(image)
def test_compressed_image2d(self):
importer = trade.ImporterManager().load_and_instantiate('DdsImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'rgba_dxt1.dds'))
image = importer.image2d(0)
self.assertTrue(image.is_compressed)
converter = trade.ImageConverterManager().load_and_instantiate('BcDecImageConverter')
# Both ImageData and CompressedImageView should work
converted1 = converter.convert(image)
converted2 = converter.convert(CompressedImageView2D(image))
self.assertFalse(converted1.is_compressed)
self.assertFalse(converted2.is_compressed)
def test_compressed_image2d_failed(self):
importer = trade.ImporterManager().load_and_instantiate('DdsImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'rgba_dxt1.dds'))
image = importer.image2d(0)
self.assertTrue(image.is_compressed)
converter = trade.ImageConverterManager().load_and_instantiate('EtcDecImageConverter')
with self.assertRaisesRegex(RuntimeError, "conversion failed"):
converter.convert(image)
def test_image2d_to_file(self): def test_image2d_to_file(self):
importer = trade.ImporterManager().load_and_instantiate('StbImageImporter') importer = trade.ImporterManager().load_and_instantiate('StbImageImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'rgb.png')) importer.open_file(os.path.join(os.path.dirname(__file__), 'rgb.png'))
@ -1687,8 +1715,11 @@ class ImageConverter(unittest.TestCase):
converter = trade.ImageConverterManager().load_and_instantiate('StbImageConverter') converter = trade.ImageConverterManager().load_and_instantiate('StbImageConverter')
with tempfile.TemporaryDirectory() as tmp: with tempfile.TemporaryDirectory() as tmp:
converter.convert_to_file(image, os.path.join(tmp, "image.png")) # Both ImageData and ImageView should work
self.assertTrue(os.path.exists(os.path.join(tmp, "image.png"))) converter.convert_to_file(image, os.path.join(tmp, "image1.png"))
converter.convert_to_file(ImageView2D(image), os.path.join(tmp, "image2.png"))
self.assertTrue(os.path.exists(os.path.join(tmp, "image1.png")))
self.assertTrue(os.path.exists(os.path.join(tmp, "image2.png")))
def test_image2d_to_file_failed(self): def test_image2d_to_file_failed(self):
importer = trade.ImporterManager().load_and_instantiate('StbImageImporter') importer = trade.ImporterManager().load_and_instantiate('StbImageImporter')
@ -1701,6 +1732,35 @@ class ImageConverter(unittest.TestCase):
with self.assertRaisesRegex(RuntimeError, "conversion failed"): with self.assertRaisesRegex(RuntimeError, "conversion failed"):
converter.convert_to_file(image, os.path.join(tmp, "image.hdr")) converter.convert_to_file(image, os.path.join(tmp, "image.hdr"))
def test_compressed_image2d_to_file(self):
importer = trade.ImporterManager().load_and_instantiate('DdsImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'rgba_dxt1.dds'))
image = importer.image2d(0)
self.assertTrue(image.is_compressed)
converter = trade.ImageConverterManager().load_and_instantiate('KtxImageConverter')
with tempfile.TemporaryDirectory() as tmp:
# Both ImageData and CompressedImageView should work
converter.convert_to_file(image, os.path.join(tmp, "image1.ktx2"))
converter.convert_to_file(CompressedImageView2D(image), os.path.join(tmp, "image2.ktx2"))
self.assertTrue(os.path.exists(os.path.join(tmp, "image1.ktx2")))
self.assertTrue(os.path.exists(os.path.join(tmp, "image2.ktx2")))
def test_compressed_image2d_to_file_failed(self):
importer = trade.ImporterManager().load_and_instantiate('DdsImporter')
importer.open_file(os.path.join(os.path.dirname(__file__), 'rgba_dxt1.dds'))
image = importer.image2d(0)
self.assertTrue(image.is_compressed)
converter = trade.ImageConverterManager().load_and_instantiate('KtxImageConverter')
# Set something stupid in the config to make it fail
converter.configuration['swizzle'] = "haha"
with tempfile.TemporaryDirectory() as tmp:
with self.assertRaisesRegex(RuntimeError, "conversion failed"):
converter.convert_to_file(image, os.path.join(tmp, "image.ktx2"))
class SceneConverter(unittest.TestCase): class SceneConverter(unittest.TestCase):
def test_scenecontents_for_importer(self): def test_scenecontents_for_importer(self):
# Silly, yes, but don't want to enable StanfordImporter just for this # Silly, yes, but don't want to enable StanfordImporter just for this

18
src/python/magnum/trade.cpp

@ -1721,12 +1721,28 @@ void trade(py::module_& m) {
}, [](Trade::AbstractImageConverter& self, Trade::ImageConverterFlag flags) { }, [](Trade::AbstractImageConverter& self, Trade::ImageConverterFlag flags) {
self.setFlags(flags); self.setFlags(flags);
}, "Converter flags") }, "Converter flags")
/* ImageData overloads should be first so they correctly dispatch to
either a compressed or a non-compressed overload. With the views
being first it'd just pick whichever of them is earliest as
ImageData is implicitly convertible to each. */
.def("convert", checkImageConverterResult<Trade::ImageData1D, Trade::ImageData1D, &Trade::AbstractImageConverter::convert>, "Convert a 1D image data", py::arg("image"))
.def("convert", checkImageConverterResult<Trade::ImageData2D, Trade::ImageData2D, &Trade::AbstractImageConverter::convert>, "Convert a 2D image data", py::arg("image"))
.def("convert", checkImageConverterResult<Trade::ImageData3D, Trade::ImageData3D, &Trade::AbstractImageConverter::convert>, "Convert a 3D image data", py::arg("image"))
.def("convert", checkImageConverterResult<Trade::ImageData1D, ImageView1D, &Trade::AbstractImageConverter::convert>, "Convert a 1D image", py::arg("image")) .def("convert", checkImageConverterResult<Trade::ImageData1D, ImageView1D, &Trade::AbstractImageConverter::convert>, "Convert a 1D image", py::arg("image"))
.def("convert", checkImageConverterResult<Trade::ImageData2D, ImageView2D, &Trade::AbstractImageConverter::convert>, "Convert a 2D image", py::arg("image")) .def("convert", checkImageConverterResult<Trade::ImageData2D, ImageView2D, &Trade::AbstractImageConverter::convert>, "Convert a 2D image", py::arg("image"))
.def("convert", checkImageConverterResult<Trade::ImageData3D, ImageView3D, &Trade::AbstractImageConverter::convert>, "Convert a 3D image", py::arg("image")) .def("convert", checkImageConverterResult<Trade::ImageData3D, ImageView3D, &Trade::AbstractImageConverter::convert>, "Convert a 3D image", py::arg("image"))
.def("convert", checkImageConverterResult<Trade::ImageData1D, CompressedImageView1D, &Trade::AbstractImageConverter::convert>, "Convert a compressed 1D image", py::arg("image"))
.def("convert", checkImageConverterResult<Trade::ImageData2D, CompressedImageView2D, &Trade::AbstractImageConverter::convert>, "Convert a compressed 2D image", py::arg("image"))
.def("convert", checkImageConverterResult<Trade::ImageData3D, CompressedImageView3D, &Trade::AbstractImageConverter::convert>, "Convert a compressed 3D image", py::arg("image"))
.def("convert_to_file", checkImageConverterResult<Trade::ImageData1D, &Trade::AbstractImageConverter::convertToFile>, "Convert a 1D image data to a file", py::arg("image"), py::arg("filename"))
.def("convert_to_file", checkImageConverterResult<Trade::ImageData2D, &Trade::AbstractImageConverter::convertToFile>, "Convert a 2D image data to a file", py::arg("image"), py::arg("filename"))
.def("convert_to_file", checkImageConverterResult<Trade::ImageData3D, &Trade::AbstractImageConverter::convertToFile>, "Convert a 3D image data to a file", py::arg("image"), py::arg("filename"))
.def("convert_to_file", checkImageConverterResult<ImageView1D, &Trade::AbstractImageConverter::convertToFile>, "Convert a 1D image to a file", py::arg("image"), py::arg("filename")) .def("convert_to_file", checkImageConverterResult<ImageView1D, &Trade::AbstractImageConverter::convertToFile>, "Convert a 1D image to a file", py::arg("image"), py::arg("filename"))
.def("convert_to_file", checkImageConverterResult<ImageView2D, &Trade::AbstractImageConverter::convertToFile>, "Convert a 2D image to a file", py::arg("image"), py::arg("filename")) .def("convert_to_file", checkImageConverterResult<ImageView2D, &Trade::AbstractImageConverter::convertToFile>, "Convert a 2D image to a file", py::arg("image"), py::arg("filename"))
.def("convert_to_file", checkImageConverterResult<ImageView3D, &Trade::AbstractImageConverter::convertToFile>, "Convert a 3D image to a file", py::arg("image"), py::arg("filename")); .def("convert_to_file", checkImageConverterResult<ImageView3D, &Trade::AbstractImageConverter::convertToFile>, "Convert a 3D image to a file", py::arg("image"), py::arg("filename"))
.def("convert_to_file", checkImageConverterResult<CompressedImageView1D, &Trade::AbstractImageConverter::convertToFile>, "Convert a compressed 1D image to a file", py::arg("image"), py::arg("filename"))
.def("convert_to_file", checkImageConverterResult<CompressedImageView2D, &Trade::AbstractImageConverter::convertToFile>, "Convert a compressed 2D image to a file", py::arg("image"), py::arg("filename"))
.def("convert_to_file", checkImageConverterResult<CompressedImageView3D, &Trade::AbstractImageConverter::convertToFile>, "Convert a compressed 3D image to a file", py::arg("image"), py::arg("filename"));
corrade::plugin(abstractImageConverter); corrade::plugin(abstractImageConverter);
py::class_<PluginManager::Manager<Trade::AbstractImageConverter>, PluginManager::AbstractManager> imageConverterManager{m, "ImageConverterManager", "Manager for image converter plugins"}; py::class_<PluginManager::Manager<Trade::AbstractImageConverter>, PluginManager::AbstractManager> imageConverterManager{m, "ImageConverterManager", "Manager for image converter plugins"};

Loading…
Cancel
Save