/* This file is part of Magnum. Copyright © 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018, 2019, 2020, 2021, 2022 Vladimír Vondruš Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ #include "AbstractImageConverter.h" #include #include #include #include #include /** @todo remove once PluginManager is -free */ #include #include #include #include "Magnum/Image.h" #include "Magnum/ImageView.h" #include "Magnum/PixelFormat.h" #include "Magnum/Trade/ImageData.h" #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT #include "Magnum/Trade/configure.h" #endif namespace Magnum { namespace Trade { std::string AbstractImageConverter::pluginInterface() { return /* [interface] */ "cz.mosra.magnum.Trade.AbstractImageConverter/0.3.1" /* [interface] */ ; } #ifndef CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT std::vector AbstractImageConverter::pluginSearchPaths() { const Containers::Optional libraryLocation = Utility::Path::libraryLocation(&pluginInterface); return PluginManager::implicitPluginSearchPaths( #ifndef MAGNUM_BUILD_STATIC libraryLocation ? *libraryLocation : Containers::String{}, #else {}, #endif #ifdef CORRADE_IS_DEBUG_BUILD MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_DIR, #else MAGNUM_PLUGINS_IMAGECONVERTER_DIR, #endif #ifdef CORRADE_IS_DEBUG_BUILD "magnum-d/" #else "magnum/" #endif "imageconverters"); } #endif AbstractImageConverter::AbstractImageConverter() = default; AbstractImageConverter::AbstractImageConverter(PluginManager::Manager& manager): PluginManager::AbstractManagingPlugin{manager} {} AbstractImageConverter::AbstractImageConverter(PluginManager::AbstractManager& manager, const std::string& plugin): PluginManager::AbstractManagingPlugin{manager, plugin} {} void AbstractImageConverter::setFlags(ImageConverterFlags flags) { _flags = flags; doSetFlags(flags); } void AbstractImageConverter::doSetFlags(ImageConverterFlags) {} void AbstractImageConverter::addFlags(ImageConverterFlags flags) { setFlags(_flags|flags); } void AbstractImageConverter::clearFlags(ImageConverterFlags flags) { setFlags(_flags & ~flags); } Containers::Optional AbstractImageConverter::convert(const ImageView1D& image) { CORRADE_ASSERT(features() & ImageConverterFeature::Convert1D, "Trade::AbstractImageConverter::convert(): 1D image conversion not supported", {}); /* Unlike with convertToData() / convertToFile(), where images are checked for having a non-zero size and being non-null, here it's explicitly allowed: - Usual format conversions / resamplings are a loop that "just works" with zero-sized dimensions. - Nullptr views could be potentially useful for converters that fill an image of desired format and size with data, such as generating a noise image or some test pattern. While it could be implemented as an importer plugin as well, the size/format would have to be passed manually through plugin-specific configuration there, which is rather annoying compared to having an API prepared directly for that. Plugin implementations that can't work with these have to do this check themselves, and since that's then a plugin-specific behavior, it should be a runtime error, not an assert. */ Containers::Optional out = doConvert(image); CORRADE_ASSERT(!out || !out->_data.deleter(), "Trade::AbstractImageConverter::convert(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Optional AbstractImageConverter::doConvert(const ImageView1D&) { CORRADE_ASSERT_UNREACHABLE("Trade::AbstractImageConverter::convert(): 1D image conversion advertised but not implemented", {}); } Containers::Optional AbstractImageConverter::convert(const ImageView2D& image) { CORRADE_ASSERT(features() & ImageConverterFeature::Convert2D, "Trade::AbstractImageConverter::convert(): 2D image conversion not supported", {}); /* No zero size / nullptr checks here, see convert(const ImageView1D&) for reasons why */ Containers::Optional out = doConvert(image); CORRADE_ASSERT(!out || !out->_data.deleter(), "Trade::AbstractImageConverter::convert(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Optional AbstractImageConverter::doConvert(const ImageView2D&) { CORRADE_ASSERT_UNREACHABLE("Trade::AbstractImageConverter::convert(): 2D image conversion advertised but not implemented", {}); } #ifdef MAGNUM_BUILD_DEPRECATED Containers::Optional AbstractImageConverter::exportToImage(const ImageView2D& image) { Containers::Optional out = convert(image); if(!out) return {}; if(out->isCompressed()) { Error{} << "Trade::AbstractImageConverter::exportToImage(): implementation returned a compressed image"; return {}; } const PixelStorage storage = out->storage(); const PixelFormat format = out->format(); const UnsignedInt formatExtra = out->formatExtra(); const UnsignedInt pixelSize = out->pixelSize(); const Vector2i size = out->size(); return Image2D{storage, format, formatExtra, pixelSize, size, out->release()}; } Containers::Optional AbstractImageConverter::exportToCompressedImage(const ImageView2D& image) { Containers::Optional out = convert(image); if(!out) return {}; if(!out->isCompressed()) { Error{} << "Trade::AbstractImageConverter::exportToCompressedImage(): implementation returned an uncompressed image"; return {}; } const CompressedPixelStorage storage = out->compressedStorage(); const CompressedPixelFormat format = out->compressedFormat(); const Vector2i size = out->size(); return CompressedImage2D{storage, format, size, out->release()}; } #endif Containers::Optional AbstractImageConverter::convert(const ImageView3D& image) { CORRADE_ASSERT(features() & ImageConverterFeature::Convert3D, "Trade::AbstractImageConverter::convert(): 3D image conversion not supported", {}); /* No zero size / nullptr checks here, see convert(const ImageView1D&) for reasons why */ Containers::Optional out = doConvert(image); CORRADE_ASSERT(!out || !out->_data.deleter(), "Trade::AbstractImageConverter::convert(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Optional AbstractImageConverter::doConvert(const ImageView3D&) { CORRADE_ASSERT_UNREACHABLE("Trade::AbstractImageConverter::convert(): 3D image conversion advertised but not implemented", {}); } Containers::Optional AbstractImageConverter::convert(const CompressedImageView1D& image) { CORRADE_ASSERT(features() & ImageConverterFeature::ConvertCompressed1D, "Trade::AbstractImageConverter::convert(): compressed 1D image conversion not supported", {}); /* No zero size / nullptr checks here, see convert(const ImageView1D&) for reasons why */ Containers::Optional out = doConvert(image); CORRADE_ASSERT(!out || !out->_data.deleter(), "Trade::AbstractImageConverter::convert(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Optional AbstractImageConverter::doConvert(const CompressedImageView1D&) { CORRADE_ASSERT_UNREACHABLE("Trade::AbstractImageConverter::convert(): compressed 1D image conversion advertised but not implemented", {}); } Containers::Optional AbstractImageConverter::convert(const CompressedImageView2D& image) { CORRADE_ASSERT(features() & ImageConverterFeature::ConvertCompressed2D, "Trade::AbstractImageConverter::convert(): compressed 2D image conversion not supported", {}); /* No zero size / nullptr checks here, see convert(const ImageView1D&) for reasons why */ Containers::Optional out = doConvert(image); CORRADE_ASSERT(!out || !out->_data.deleter(), "Trade::AbstractImageConverter::convert(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Optional AbstractImageConverter::doConvert(const CompressedImageView2D&) { CORRADE_ASSERT_UNREACHABLE("Trade::AbstractImageConverter::convert(): compressed 2D image conversion advertised but not implemented", {}); } Containers::Optional AbstractImageConverter::convert(const CompressedImageView3D& image) { CORRADE_ASSERT(features() & ImageConverterFeature::ConvertCompressed3D, "Trade::AbstractImageConverter::convert(): compressed 3D image conversion not supported", {}); /* No zero size / nullptr checks here, see convert(const ImageView1D&) for reasons why */ Containers::Optional out = doConvert(image); CORRADE_ASSERT(!out || !out->_data.deleter(), "Trade::AbstractImageConverter::convert(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Optional AbstractImageConverter::doConvert(const CompressedImageView3D&) { CORRADE_ASSERT_UNREACHABLE("Trade::AbstractImageConverter::convert(): compressed 3D image conversion advertised but not implemented", {}); } Containers::Optional AbstractImageConverter::convert(const ImageData1D& image) { return image.isCompressed() ? convert(CompressedImageView1D(image)) : convert(ImageView1D(image)); } Containers::Optional AbstractImageConverter::convert(const ImageData2D& image) { return image.isCompressed() ? convert(CompressedImageView2D(image)) : convert(ImageView2D(image)); } Containers::Optional AbstractImageConverter::convert(const ImageData3D& image) { return image.isCompressed() ? convert(CompressedImageView3D(image)) : convert(ImageView3D(image)); } #ifndef CORRADE_NO_ASSERT namespace { template class View> bool checkImageValidity(const char* const messagePrefix, const View& image) { /* At some point there might be a file format that allows zero-sized images, but so far I don't know about any. When such format appears, this check will get moved to plugin implementations that can't work with zero-sized images. Also note that this check isn't done for the Image->Image conversion above, there zero-sized images and nullptr *could* make sense. */ CORRADE_ASSERT(image.size().product(), messagePrefix << "can't convert image with a zero size:" << image.size(), false); CORRADE_ASSERT(image.data(), messagePrefix << "can't convert image with a nullptr view", false); return true; } } #endif Containers::Array AbstractImageConverter::convertToData(const ImageView1D& image) { CORRADE_ASSERT(features() >= ImageConverterFeature::Convert1DToData, "Trade::AbstractImageConverter::convertToData(): 1D image conversion not supported", nullptr); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToData():", image)) return {}; #endif Containers::Array out = doConvertToData(image); CORRADE_ASSERT(!out.deleter(), "Trade::AbstractImageConverter::convertToData(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Array AbstractImageConverter::doConvertToData(const ImageView1D& image) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertLevels1DToData, "Trade::AbstractImageConverter::convertToData(): 1D image conversion advertised but not implemented", nullptr); return doConvertToData(Containers::arrayView({image})); } Containers::Array AbstractImageConverter::convertToData(const ImageView2D& image) { CORRADE_ASSERT(features() >= ImageConverterFeature::Convert2DToData, "Trade::AbstractImageConverter::convertToData(): 2D image conversion not supported", nullptr); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToData():", image)) return {}; #endif Containers::Array out = doConvertToData(image); CORRADE_ASSERT(!out.deleter(), "Trade::AbstractImageConverter::convertToData(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Array AbstractImageConverter::doConvertToData(const ImageView2D& image) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertLevels2DToData, "Trade::AbstractImageConverter::convertToData(): 2D image conversion advertised but not implemented", nullptr); return doConvertToData(Containers::arrayView({image})); } #ifdef MAGNUM_BUILD_DEPRECATED Containers::Array AbstractImageConverter::exportToData(const ImageView2D& image) { return convertToData(image); } #endif Containers::Array AbstractImageConverter::convertToData(const ImageView3D& image) { CORRADE_ASSERT(features() >= ImageConverterFeature::Convert3DToData, "Trade::AbstractImageConverter::convertToData(): 3D image conversion not supported", nullptr); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToData():", image)) return {}; #endif Containers::Array out = doConvertToData(image); CORRADE_ASSERT(!out.deleter(), "Trade::AbstractImageConverter::convertToData(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Array AbstractImageConverter::doConvertToData(const ImageView3D& image) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertLevels3DToData, "Trade::AbstractImageConverter::convertToData(): 3D image conversion advertised but not implemented", nullptr); return doConvertToData(Containers::arrayView({image})); } Containers::Array AbstractImageConverter::convertToData(const CompressedImageView1D& image) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertCompressed1DToData, "Trade::AbstractImageConverter::convertToData(): compressed 1D image conversion not supported", nullptr); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToData():", image)) return {}; #endif Containers::Array out = doConvertToData(image); CORRADE_ASSERT(!out.deleter(), "Trade::AbstractImageConverter::convertToData(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Array AbstractImageConverter::doConvertToData(const CompressedImageView1D& image) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertCompressedLevels1DToData, "Trade::AbstractImageConverter::convertToData(): compressed 1D image conversion advertised but not implemented", nullptr); return doConvertToData(Containers::arrayView({image})); } Containers::Array AbstractImageConverter::convertToData(const CompressedImageView2D& image) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertCompressed2DToData, "Trade::AbstractImageConverter::convertToData(): compressed 2D image conversion not supported", nullptr); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToData():", image)) return {}; #endif Containers::Array out = doConvertToData(image); CORRADE_ASSERT(!out.deleter(), "Trade::AbstractImageConverter::convertToData(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Array AbstractImageConverter::doConvertToData(const CompressedImageView2D& image) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertCompressedLevels2DToData, "Trade::AbstractImageConverter::convertToData(): compressed 2D image conversion advertised but not implemented", nullptr); return doConvertToData(Containers::arrayView({image})); } #ifdef MAGNUM_BUILD_DEPRECATED Containers::Array AbstractImageConverter::exportToData(const CompressedImageView2D& image) { return convertToData(image); } #endif Containers::Array AbstractImageConverter::convertToData(const CompressedImageView3D& image) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertCompressed3DToData, "Trade::AbstractImageConverter::convertToData(): compressed 3D image conversion not supported", nullptr); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToData():", image)) return {}; #endif Containers::Array out = doConvertToData(image); CORRADE_ASSERT(!out.deleter(), "Trade::AbstractImageConverter::convertToData(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Array AbstractImageConverter::doConvertToData(const CompressedImageView3D& image) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertCompressedLevels3DToData, "Trade::AbstractImageConverter::convertToData(): compressed 3D image conversion advertised but not implemented", nullptr); return doConvertToData(Containers::arrayView({image})); } Containers::Array AbstractImageConverter::convertToData(const ImageData1D& image) { return image.isCompressed() ? convertToData(CompressedImageView1D(image)) : convertToData(ImageView1D(image)); } Containers::Array AbstractImageConverter::convertToData(const ImageData2D& image) { return image.isCompressed() ? convertToData(CompressedImageView2D(image)) : convertToData(ImageView2D(image)); } #ifdef MAGNUM_BUILD_DEPRECATED Containers::Array AbstractImageConverter::exportToData(const ImageData2D& image) { return convertToData(image); } #endif Containers::Array AbstractImageConverter::convertToData(const ImageData3D& image) { return image.isCompressed() ? convertToData(CompressedImageView3D(image)) : convertToData(ImageView3D(image)); } #ifndef CORRADE_NO_ASSERT namespace { template bool checkImageValidity(const char* const messagePrefix, const Containers::ArrayView> imageLevels) { CORRADE_ASSERT(!imageLevels.empty(), messagePrefix << "at least one image has to be specified", false); const PixelFormat format = imageLevels[0].format(); const UnsignedInt formatExtra = imageLevels[0].formatExtra(); /* Going through *all* levels although the format assertion is never fired in the first iteration in order to properly check also the first one for zero size / nullptr. */ for(std::size_t i = 0; i != imageLevels.size(); ++i) { CORRADE_ASSERT(imageLevels[i].size().product(), messagePrefix << "can't convert image" << i << "with a zero size:" << imageLevels[i].size(), false); CORRADE_ASSERT(imageLevels[i].data(), messagePrefix << "can't convert image" << i << "with a nullptr view", false); CORRADE_ASSERT(imageLevels[i].format() == format, messagePrefix << "levels don't have the same format, expected" << format << "but got" << imageLevels[i].format() << "for image" << i, false); CORRADE_ASSERT(imageLevels[i].formatExtra() == formatExtra, messagePrefix << "levels don't have the same extra format field, expected" << formatExtra << "but got" << imageLevels[i].formatExtra() << "for image" << i, false); } return true; } template bool checkImageValidity(const char* const messagePrefix, const Containers::ArrayView> imageLevels) { CORRADE_ASSERT(!imageLevels.empty(), messagePrefix << "at least one image has to be specified", false); const CompressedPixelFormat format = imageLevels[0].format(); /* Going through *all* levels although the format assertion is never fired in the first iteration in order to properly check also the first one for zero size / nullptr. */ for(std::size_t i = 0; i != imageLevels.size(); ++i) { CORRADE_ASSERT(imageLevels[i].size().product(), messagePrefix << "can't convert image" << i << "with a zero size:" << imageLevels[i].size(), false); CORRADE_ASSERT(imageLevels[i].data(), messagePrefix << "can't convert image" << i << "with a nullptr view", false); CORRADE_ASSERT(imageLevels[i].format() == format, messagePrefix << "levels don't have the same format, expected" << format << "but got" << imageLevels[i].format() << "for image" << i, false); } return true; } } #endif Containers::Array AbstractImageConverter::convertToData(const Containers::ArrayView imageLevels) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertLevels1DToData, "Trade::AbstractImageConverter::convertToData(): multi-level 1D image conversion not supported", nullptr); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToData():", imageLevels)) return {}; #endif Containers::Array out = doConvertToData(imageLevels); CORRADE_ASSERT(!out.deleter(), "Trade::AbstractImageConverter::convertToData(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Array AbstractImageConverter::convertToData(const std::initializer_list imageLevels) { return convertToData(Containers::arrayView(imageLevels)); } Containers::Array AbstractImageConverter::doConvertToData(Containers::ArrayView) { CORRADE_ASSERT_UNREACHABLE("Trade::AbstractImageConverter::convertToData(): multi-level 1D image conversion advertised but not implemented", nullptr); } Containers::Array AbstractImageConverter::convertToData(const Containers::ArrayView imageLevels) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertLevels2DToData, "Trade::AbstractImageConverter::convertToData(): multi-level 2D image conversion not supported", nullptr); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToData():", imageLevels)) return {}; #endif Containers::Array out = doConvertToData(imageLevels); CORRADE_ASSERT(!out.deleter(), "Trade::AbstractImageConverter::convertToData(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Array AbstractImageConverter::convertToData(const std::initializer_list imageLevels) { return convertToData(Containers::arrayView(imageLevels)); } Containers::Array AbstractImageConverter::doConvertToData(Containers::ArrayView) { CORRADE_ASSERT_UNREACHABLE("Trade::AbstractImageConverter::convertToData(): multi-level 2D image conversion advertised but not implemented", nullptr); } Containers::Array AbstractImageConverter::convertToData(const Containers::ArrayView imageLevels) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertLevels3DToData, "Trade::AbstractImageConverter::convertToData(): multi-level 3D image conversion not supported", nullptr); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToData():", imageLevels)) return {}; #endif Containers::Array out = doConvertToData(imageLevels); CORRADE_ASSERT(!out.deleter(), "Trade::AbstractImageConverter::convertToData(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Array AbstractImageConverter::convertToData(const std::initializer_list imageLevels) { return convertToData(Containers::arrayView(imageLevels)); } Containers::Array AbstractImageConverter::doConvertToData(Containers::ArrayView) { CORRADE_ASSERT_UNREACHABLE("Trade::AbstractImageConverter::convertToData(): multi-level 3D image conversion advertised but not implemented", nullptr); } Containers::Array AbstractImageConverter::convertToData(const Containers::ArrayView imageLevels) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertCompressedLevels1DToData, "Trade::AbstractImageConverter::convertToData(): multi-level compressed 1D image conversion not supported", nullptr); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToData():", imageLevels)) return {}; #endif Containers::Array out = doConvertToData(imageLevels); CORRADE_ASSERT(!out.deleter(), "Trade::AbstractImageConverter::convertToData(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Array AbstractImageConverter::convertToData(const std::initializer_list imageLevels) { return convertToData(Containers::arrayView(imageLevels)); } Containers::Array AbstractImageConverter::doConvertToData(Containers::ArrayView) { CORRADE_ASSERT_UNREACHABLE("Trade::AbstractImageConverter::convertToData(): multi-level compressed 1D image conversion advertised but not implemented", nullptr); } Containers::Array AbstractImageConverter::convertToData(const Containers::ArrayView imageLevels) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertCompressedLevels2DToData, "Trade::AbstractImageConverter::convertToData(): multi-level compressed 2D image conversion not supported", nullptr); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToData():", imageLevels)) return {}; #endif Containers::Array out = doConvertToData(imageLevels); CORRADE_ASSERT(!out.deleter(), "Trade::AbstractImageConverter::convertToData(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Array AbstractImageConverter::convertToData(const std::initializer_list imageLevels) { return convertToData(Containers::arrayView(imageLevels)); } Containers::Array AbstractImageConverter::doConvertToData(Containers::ArrayView) { CORRADE_ASSERT_UNREACHABLE("Trade::AbstractImageConverter::convertToData(): multi-level compressed 2D image conversion advertised but not implemented", nullptr); } Containers::Array AbstractImageConverter::convertToData(const Containers::ArrayView imageLevels) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertCompressedLevels3DToData, "Trade::AbstractImageConverter::convertToData(): multi-level compressed 3D image conversion not supported", nullptr); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToData():", imageLevels)) return {}; #endif Containers::Array out = doConvertToData(imageLevels); CORRADE_ASSERT(!out.deleter(), "Trade::AbstractImageConverter::convertToData(): implementation is not allowed to use a custom Array deleter", {}); return out; } Containers::Array AbstractImageConverter::convertToData(const std::initializer_list imageLevels) { return convertToData(Containers::arrayView(imageLevels)); } Containers::Array AbstractImageConverter::doConvertToData(Containers::ArrayView) { CORRADE_ASSERT_UNREACHABLE("Trade::AbstractImageConverter::convertToData(): multi-level compressed 3D image conversion advertised but not implemented", nullptr); } bool AbstractImageConverter::convertToFile(const ImageView1D& image, const Containers::StringView filename) { CORRADE_ASSERT(features() & ImageConverterFeature::Convert1DToFile, "Trade::AbstractImageConverter::convertToFile(): 1D image conversion not supported", {}); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToFile():", image)) return {}; #endif return doConvertToFile(image, filename); } bool AbstractImageConverter::doConvertToFile(const ImageView1D& image, const Containers::StringView filename) { /* Prefer to go through the ToFile variant instead of ToData assuming that it could have a more memory-efficient implementation than having to materialize the whole output in memory first */ if(features() >= ImageConverterFeature::ConvertLevels1DToFile) return doConvertToFile(Containers::arrayView({image}), filename); CORRADE_ASSERT(features() >= ImageConverterFeature::Convert1DToData, "Trade::AbstractImageConverter::convertToFile(): 1D image conversion advertised but not implemented", false); const auto data = doConvertToData(image); /* No deleter checks as it doesn't matter here */ if(!data) return false; if(!Utility::Path::write(filename, data)) { Error() << "Trade::AbstractImageConverter::convertToFile(): cannot write to file" << filename; return false; } return true; } bool AbstractImageConverter::convertToFile(const ImageView2D& image, const Containers::StringView filename) { CORRADE_ASSERT(features() & ImageConverterFeature::Convert2DToFile, "Trade::AbstractImageConverter::convertToFile(): 2D image conversion not supported", {}); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToFile():", image)) return {}; #endif return doConvertToFile(image, filename); } bool AbstractImageConverter::doConvertToFile(const ImageView2D& image, const Containers::StringView filename) { /* Prefer to go through the ToFile variant instead of ToData assuming that it could have a more memory-efficient implementation than having to materialize the whole output in memory first */ if(features() >= ImageConverterFeature::ConvertLevels2DToFile) return doConvertToFile(Containers::arrayView({image}), filename); CORRADE_ASSERT(features() >= ImageConverterFeature::Convert2DToData, "Trade::AbstractImageConverter::convertToFile(): 2D image conversion advertised but not implemented", false); const auto data = doConvertToData(image); /* No deleter checks as it doesn't matter here */ if(!data) return false; if(!Utility::Path::write(filename, data)) { Error() << "Trade::AbstractImageConverter::convertToFile(): cannot write to file" << filename; return false; } return true; } #ifdef MAGNUM_BUILD_DEPRECATED bool AbstractImageConverter::exportToFile(const ImageView2D& image, const std::string& filename) { return convertToFile(image, filename); } #endif bool AbstractImageConverter::convertToFile(const ImageView3D& image, const Containers::StringView filename) { CORRADE_ASSERT(features() & ImageConverterFeature::Convert3DToFile, "Trade::AbstractImageConverter::convertToFile(): 3D image conversion not supported", {}); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToFile():", image)) return {}; #endif return doConvertToFile(image, filename); } bool AbstractImageConverter::doConvertToFile(const ImageView3D& image, const Containers::StringView filename) { /* Prefer to go through the ToFile variant instead of ToData assuming that it could have a more memory-efficient implementation than having to materialize the whole output in memory first */ if(features() >= ImageConverterFeature::ConvertLevels3DToFile) return doConvertToFile(Containers::arrayView({image}), filename); CORRADE_ASSERT(features() >= ImageConverterFeature::Convert3DToData, "Trade::AbstractImageConverter::convertToFile(): 3D image conversion advertised but not implemented", false); const auto data = doConvertToData(image); /* No deleter checks as it doesn't matter here */ if(!data) return false; if(!Utility::Path::write(filename, data)) { Error() << "Trade::AbstractImageConverter::convertToFile(): cannot write to file" << filename; return false; } return true; } bool AbstractImageConverter::convertToFile(const CompressedImageView1D& image, const Containers::StringView filename) { CORRADE_ASSERT(features() & ImageConverterFeature::ConvertCompressed1DToFile, "Trade::AbstractImageConverter::convertToFile(): compressed 1D image conversion not supported", {}); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToFile():", image)) return {}; #endif return doConvertToFile(image, filename); } bool AbstractImageConverter::doConvertToFile(const CompressedImageView1D& image, Containers::StringView filename) { /* Prefer to go through the ToFile variant instead of ToData assuming that it could have a more memory-efficient implementation than having to materialize the whole output in memory first */ if(features() >= ImageConverterFeature::ConvertCompressedLevels1DToFile) return doConvertToFile(Containers::arrayView({image}), filename); CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertCompressed1DToData, "Trade::AbstractImageConverter::convertToFile(): compressed 1D image conversion advertised but not implemented", false); const auto data = doConvertToData(image); /* No deleter checks as it doesn't matter here */ if(!data) return false; if(!Utility::Path::write(filename, data)) { Error() << "Trade::AbstractImageConverter::convertToFile(): cannot write to file" << filename; return false; } return true; } bool AbstractImageConverter::convertToFile(const CompressedImageView2D& image, const Containers::StringView filename) { CORRADE_ASSERT(features() & ImageConverterFeature::ConvertCompressed2DToFile, "Trade::AbstractImageConverter::convertToFile(): compressed 2D image conversion not supported", {}); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToFile():", image)) return {}; #endif return doConvertToFile(image, filename); } bool AbstractImageConverter::doConvertToFile(const CompressedImageView2D& image, Containers::StringView filename) { /* Prefer to go through the ToFile variant instead of ToData assuming that it could have a more memory-efficient implementation than having to materialize the whole output in memory first */ if(features() >= ImageConverterFeature::ConvertCompressedLevels2DToFile) return doConvertToFile(Containers::arrayView({image}), filename); CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertCompressed2DToData, "Trade::AbstractImageConverter::convertToFile(): compressed 2D image conversion advertised but not implemented", false); const auto data = doConvertToData(image); /* No deleter checks as it doesn't matter here */ if(!data) return false; if(!Utility::Path::write(filename, data)) { Error() << "Trade::AbstractImageConverter::convertToFile(): cannot write to file" << filename; return false; } return true; } #ifdef MAGNUM_BUILD_DEPRECATED bool AbstractImageConverter::exportToFile(const CompressedImageView2D& image, const std::string& filename) { return convertToFile(image, filename); } #endif bool AbstractImageConverter::convertToFile(const CompressedImageView3D& image, const Containers::StringView filename) { CORRADE_ASSERT(features() & ImageConverterFeature::ConvertCompressed3DToFile, "Trade::AbstractImageConverter::convertToFile(): compressed 3D image conversion not supported", {}); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToFile():", image)) return {}; #endif return doConvertToFile(image, filename); } bool AbstractImageConverter::doConvertToFile(const CompressedImageView3D& image, Containers::StringView filename) { /* Prefer to go through the ToFile variant instead of ToData assuming that it could have a more memory-efficient implementation than having to materialize the whole output in memory first */ if(features() >= ImageConverterFeature::ConvertCompressedLevels3DToFile) return doConvertToFile(Containers::arrayView({image}), filename); CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertCompressed3DToData, "Trade::AbstractImageConverter::convertToFile(): compressed 3D image conversion advertised but not implemented", false); const auto data = doConvertToData(image); /* No deleter checks as it doesn't matter here */ if(!data) return false; if(!Utility::Path::write(filename, data)) { Error() << "Trade::AbstractImageConverter::convertToFile(): cannot write to file" << filename; return false; } return true; } bool AbstractImageConverter::convertToFile(const ImageData1D& image, const Containers::StringView filename) { return image.isCompressed() ? convertToFile(CompressedImageView1D(image), filename) : convertToFile(ImageView1D(image), filename); } bool AbstractImageConverter::convertToFile(const ImageData2D& image, const Containers::StringView filename) { return image.isCompressed() ? convertToFile(CompressedImageView2D(image), filename) : convertToFile(ImageView2D(image), filename); } #ifdef MAGNUM_BUILD_DEPRECATED bool AbstractImageConverter::exportToFile(const ImageData2D& image, const std::string& filename) { return convertToFile(image, filename); } #endif bool AbstractImageConverter::convertToFile(const ImageData3D& image, const Containers::StringView filename) { return image.isCompressed() ? convertToFile(CompressedImageView3D(image), filename) : convertToFile(ImageView3D(image), filename); } bool AbstractImageConverter::convertToFile(const Containers::ArrayView imageLevels, const Containers::StringView filename) { CORRADE_ASSERT(features() & ImageConverterFeature::ConvertLevels1DToFile, "Trade::AbstractImageConverter::convertToFile(): multi-level 1D image conversion not supported", {}); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToFile():", imageLevels)) return {}; #endif return doConvertToFile(imageLevels, filename); } bool AbstractImageConverter::convertToFile(const std::initializer_list imageLevels, const Containers::StringView filename) { return convertToFile(Containers::arrayView(imageLevels), filename); } bool AbstractImageConverter::doConvertToFile(const Containers::ArrayView imageLevels, const Containers::StringView filename) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertLevels1DToData, "Trade::AbstractImageConverter::convertToFile(): multi-level 1D image conversion advertised but not implemented", false); const auto data = doConvertToData(imageLevels); /* No deleter checks as it doesn't matter here */ if(!data) return false; if(!Utility::Path::write(filename, data)) { Error() << "Trade::AbstractImageConverter::convertToFile(): cannot write to file" << filename; return false; } return true; } bool AbstractImageConverter::convertToFile(const Containers::ArrayView imageLevels, const Containers::StringView filename) { CORRADE_ASSERT(features() & ImageConverterFeature::ConvertLevels2DToFile, "Trade::AbstractImageConverter::convertToFile(): multi-level 2D image conversion not supported", {}); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToFile():", imageLevels)) return {}; #endif return doConvertToFile(imageLevels, filename); } bool AbstractImageConverter::convertToFile(const std::initializer_list imageLevels, const Containers::StringView filename) { return convertToFile(Containers::arrayView(imageLevels), filename); } bool AbstractImageConverter::doConvertToFile(const Containers::ArrayView imageLevels, const Containers::StringView filename) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertLevels2DToData, "Trade::AbstractImageConverter::convertToFile(): multi-level 2D image conversion advertised but not implemented", false); const auto data = doConvertToData(imageLevels); /* No deleter checks as it doesn't matter here */ if(!data) return false; if(!Utility::Path::write(filename, data)) { Error() << "Trade::AbstractImageConverter::convertToFile(): cannot write to file" << filename; return false; } return true; } bool AbstractImageConverter::convertToFile(const Containers::ArrayView imageLevels, const Containers::StringView filename) { CORRADE_ASSERT(features() & ImageConverterFeature::ConvertLevels3DToFile, "Trade::AbstractImageConverter::convertToFile(): multi-level 3D image conversion not supported", {}); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToFile():", imageLevels)) return {}; #endif return doConvertToFile(imageLevels, filename); } bool AbstractImageConverter::convertToFile(const std::initializer_list imageLevels, const Containers::StringView filename) { return convertToFile(Containers::arrayView(imageLevels), filename); } bool AbstractImageConverter::doConvertToFile(const Containers::ArrayView imageLevels, const Containers::StringView filename) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertLevels3DToData, "Trade::AbstractImageConverter::convertToFile(): multi-level 3D image conversion advertised but not implemented", false); const auto data = doConvertToData(imageLevels); /* No deleter checks as it doesn't matter here */ if(!data) return false; if(!Utility::Path::write(filename, data)) { Error() << "Trade::AbstractImageConverter::convertToFile(): cannot write to file" << filename; return false; } return true; } bool AbstractImageConverter::convertToFile(const Containers::ArrayView imageLevels, const Containers::StringView filename) { CORRADE_ASSERT(features() & ImageConverterFeature::ConvertCompressedLevels1DToFile, "Trade::AbstractImageConverter::convertToFile(): multi-level compressed 1D image conversion not supported", {}); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToFile():", imageLevels)) return {}; #endif return doConvertToFile(imageLevels, filename); } bool AbstractImageConverter::convertToFile(const std::initializer_list imageLevels, const Containers::StringView filename) { return convertToFile(Containers::arrayView(imageLevels), filename); } bool AbstractImageConverter::doConvertToFile(const Containers::ArrayView imageLevels, Containers::StringView filename) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertCompressedLevels1DToData, "Trade::AbstractImageConverter::convertToFile(): multi-level compressed 1D image conversion advertised but not implemented", false); const auto data = doConvertToData(imageLevels); /* No deleter checks as it doesn't matter here */ if(!data) return false; if(!Utility::Path::write(filename, data)) { Error() << "Trade::AbstractImageConverter::convertToFile(): cannot write to file" << filename; return false; } return true; } bool AbstractImageConverter::convertToFile(const Containers::ArrayView imageLevels, const Containers::StringView filename) { CORRADE_ASSERT(features() & ImageConverterFeature::ConvertCompressedLevels2DToFile, "Trade::AbstractImageConverter::convertToFile(): multi-level compressed 2D image conversion not supported", {}); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToFile():", imageLevels)) return {}; #endif return doConvertToFile(imageLevels, filename); } bool AbstractImageConverter::convertToFile(const std::initializer_list imageLevels, const Containers::StringView filename) { return convertToFile(Containers::arrayView(imageLevels), filename); } bool AbstractImageConverter::doConvertToFile(const Containers::ArrayView imageLevels, Containers::StringView filename) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertCompressedLevels2DToData, "Trade::AbstractImageConverter::convertToFile(): multi-level compressed 2D image conversion advertised but not implemented", false); const auto data = doConvertToData(imageLevels); /* No deleter checks as it doesn't matter here */ if(!data) return false; if(!Utility::Path::write(filename, data)) { Error() << "Trade::AbstractImageConverter::convertToFile(): cannot write to file" << filename; return false; } return true; } bool AbstractImageConverter::convertToFile(const Containers::ArrayView imageLevels, const Containers::StringView filename) { CORRADE_ASSERT(features() & ImageConverterFeature::ConvertCompressedLevels3DToFile, "Trade::AbstractImageConverter::convertToFile(): multi-level compressed 3D image conversion not supported", {}); #ifndef CORRADE_NO_ASSERT /* Explicitly return if checks fail for CORRADE_GRACEFUL_ASSERT builds */ if(!checkImageValidity("Trade::AbstractImageConverter::convertToFile():", imageLevels)) return {}; #endif return doConvertToFile(imageLevels, filename); } bool AbstractImageConverter::convertToFile(const std::initializer_list imageLevels, const Containers::StringView filename) { return convertToFile(Containers::arrayView(imageLevels), filename); } bool AbstractImageConverter::doConvertToFile(const Containers::ArrayView imageLevels, Containers::StringView filename) { CORRADE_ASSERT(features() >= ImageConverterFeature::ConvertCompressedLevels3DToData, "Trade::AbstractImageConverter::convertToFile(): multi-level compressed 3D image conversion advertised but not implemented", false); const auto data = doConvertToData(imageLevels); /* No deleter checks as it doesn't matter here */ if(!data) return false; if(!Utility::Path::write(filename, data)) { Error() << "Trade::AbstractImageConverter::convertToFile(): cannot write to file" << filename; return false; } return true; } Debug& operator<<(Debug& debug, const ImageConverterFeature value) { debug << "Trade::ImageConverterFeature" << Debug::nospace; switch(value) { /* LCOV_EXCL_START */ #define _c(v) case ImageConverterFeature::v: return debug << "::" #v; _c(Convert1D) _c(Convert2D) _c(Convert3D) _c(ConvertCompressed1D) _c(ConvertCompressed2D) _c(ConvertCompressed3D) _c(Convert1DToFile) _c(Convert2DToFile) _c(Convert3DToFile) _c(ConvertCompressed1DToFile) _c(ConvertCompressed2DToFile) _c(ConvertCompressed3DToFile) _c(Convert1DToData) _c(Convert2DToData) _c(Convert3DToData) _c(ConvertCompressed1DToData) _c(ConvertCompressed2DToData) _c(ConvertCompressed3DToData) _c(ConvertLevels1DToFile) _c(ConvertLevels2DToFile) _c(ConvertLevels3DToFile) _c(ConvertCompressedLevels1DToFile) _c(ConvertCompressedLevels2DToFile) _c(ConvertCompressedLevels3DToFile) _c(ConvertLevels1DToData) _c(ConvertLevels2DToData) _c(ConvertLevels3DToData) _c(ConvertCompressedLevels1DToData) _c(ConvertCompressedLevels2DToData) _c(ConvertCompressedLevels3DToData) #undef _c /* LCOV_EXCL_STOP */ } return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedInt(value)) << Debug::nospace << ")"; } Debug& operator<<(Debug& debug, const ImageConverterFeatures value) { return Containers::enumSetDebugOutput(debug, value, "Trade::ImageConverterFeatures{}", { ImageConverterFeature::Convert1D, ImageConverterFeature::Convert2D, ImageConverterFeature::Convert3D, ImageConverterFeature::ConvertCompressed1D, ImageConverterFeature::ConvertCompressed2D, ImageConverterFeature::ConvertCompressed3D, ImageConverterFeature::ConvertLevels1DToData, ImageConverterFeature::ConvertLevels2DToData, ImageConverterFeature::ConvertLevels3DToData, ImageConverterFeature::ConvertCompressedLevels1DToData, ImageConverterFeature::ConvertCompressedLevels2DToData, ImageConverterFeature::ConvertCompressedLevels3DToData, /* These 6 are implied by Convert[Compressed]LevelsToData, so have to be after */ ImageConverterFeature::ConvertLevels1DToFile, ImageConverterFeature::ConvertLevels2DToFile, ImageConverterFeature::ConvertLevels3DToFile, ImageConverterFeature::ConvertCompressedLevels1DToFile, ImageConverterFeature::ConvertCompressedLevels2DToFile, ImageConverterFeature::ConvertCompressedLevels3DToFile, /* These 12 are implied by Convert[Compressed]LevelsTo{File,Data}, so have to be after */ ImageConverterFeature::Convert1DToData, ImageConverterFeature::Convert2DToData, ImageConverterFeature::Convert3DToData, ImageConverterFeature::ConvertCompressed1DToData, ImageConverterFeature::ConvertCompressed2DToData, ImageConverterFeature::ConvertCompressed3DToData, /* These 6 are implied by Convert[Compressed]ToData, so have to be after */ ImageConverterFeature::Convert1DToFile, ImageConverterFeature::Convert2DToFile, ImageConverterFeature::Convert3DToFile, ImageConverterFeature::ConvertCompressed1DToFile, ImageConverterFeature::ConvertCompressed2DToFile, ImageConverterFeature::ConvertCompressed3DToFile}); } Debug& operator<<(Debug& debug, const ImageConverterFlag value) { debug << "Trade::ImageConverterFlag" << Debug::nospace; switch(value) { /* LCOV_EXCL_START */ #define _c(v) case ImageConverterFlag::v: return debug << "::" #v; _c(Verbose) #undef _c /* LCOV_EXCL_STOP */ } return debug << "(" << Debug::nospace << reinterpret_cast(UnsignedByte(value)) << Debug::nospace << ")"; } Debug& operator<<(Debug& debug, const ImageConverterFlags value) { return Containers::enumSetDebugOutput(debug, value, "Trade::ImageConverterFlags{}", { ImageConverterFlag::Verbose}); } }}