Browse Source

{scene,image}converter: add --info options for plugins.

Lists features, aliases as well as documented contents of the whole
configuration file. Useful to not need to look up online docs when
working on the command line.
pull/595/head
Vladimír Vondruš 4 years ago
parent
commit
cc9159d387
  1. 6
      doc/changelog.dox
  2. 6
      src/Magnum/SceneTools/Implementation/sceneConverterUtilities.h
  3. 15
      src/Magnum/SceneTools/Test/CMakeLists.txt
  4. 53
      src/Magnum/SceneTools/Test/SceneConverterImplementationTest.cpp
  5. 38
      src/Magnum/SceneTools/Test/SceneConverterTest.cpp
  6. 6
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-converter.txt
  7. 11
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-image-converter.txt
  8. 5
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-importer-ignored-input-output.txt
  9. 5
      src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-importer.txt
  10. 1
      src/Magnum/SceneTools/Test/configure.h.cmake
  11. 116
      src/Magnum/SceneTools/sceneconverter.cpp
  12. 103
      src/Magnum/Trade/Implementation/converterUtilities.h
  13. 38
      src/Magnum/Trade/Test/CMakeLists.txt
  14. 315
      src/Magnum/Trade/Test/ImageConverterImplementationTest.cpp
  15. 27
      src/Magnum/Trade/Test/ImageConverterTest.cpp
  16. 11
      src/Magnum/Trade/Test/ImageConverterTestFiles/info-converter.txt
  17. 6
      src/Magnum/Trade/Test/ImageConverterTestFiles/info-importer-ignored-input-output.txt
  18. 6
      src/Magnum/Trade/Test/ImageConverterTestFiles/info-importer.txt
  19. 7
      src/Magnum/Trade/Test/configure.h.cmake
  20. 95
      src/Magnum/Trade/imageconverter.cpp

6
doc/changelog.dox

@ -229,6 +229,9 @@ See also:
correctly handle all corner cases yet and may assert on certain inputs. correctly handle all corner cases yet and may assert on certain inputs.
- The @ref magnum-sceneconverter "magnum-sceneconverter" `--info` output is - The @ref magnum-sceneconverter "magnum-sceneconverter" `--info` output is
now more compact and colored for better readability now more compact and colored for better readability
- Added `--info-importer`, `--info-converter` and `--info-image-converter`
options to @ref magnum-sceneconverter "magnum-sceneconverter", listing
plugin features and configuration file contents
@subsubsection changelog-latest-new-shaders Shaders library @subsubsection changelog-latest-new-shaders Shaders library
@ -321,6 +324,9 @@ See also:
converters together converters together
- The @ref magnum-imageconverter "magnum-imageconverter" `--info` output is - The @ref magnum-imageconverter "magnum-imageconverter" `--info` output is
now more compact and colored for better readability now more compact and colored for better readability
- Added `--info-importer` and `--info-converter` options to
@ref magnum-imageconverter "magnum-imageconverter", listing plugin features
and configuration file contents
@subsubsection changelog-latest-new-vk Vk library @subsubsection changelog-latest-new-vk Vk library

6
src/Magnum/SceneTools/Implementation/sceneConverterUtilities.h

@ -33,6 +33,7 @@
#include <Corrade/Utility/Arguments.h> #include <Corrade/Utility/Arguments.h>
#include "Magnum/Math/FunctionsBatch.h" #include "Magnum/Math/FunctionsBatch.h"
#include "Magnum/Trade/AbstractSceneConverter.h"
#include "Magnum/Trade/AnimationData.h" #include "Magnum/Trade/AnimationData.h"
#include "Magnum/Trade/CameraData.h" #include "Magnum/Trade/CameraData.h"
#include "Magnum/Trade/LightData.h" #include "Magnum/Trade/LightData.h"
@ -52,6 +53,11 @@ using namespace Containers::Literals;
particular magnum-sceneconverter and its tests */ particular magnum-sceneconverter and its tests */
namespace { namespace {
void printSceneConverterInfo(const Debug::Flags useColor, const Trade::AbstractSceneConverter& converter) {
Trade::Implementation::printPluginInfo(useColor, converter);
Trade::Implementation::printPluginConfigurationInfo(useColor, converter);
}
/** @todo const Array& doesn't work, minmax() would fail to match */ /** @todo const Array& doesn't work, minmax() would fail to match */
template<class T> Containers::String calculateBounds(Containers::Array<T>&& attribute) { template<class T> Containers::String calculateBounds(Containers::Array<T>&& attribute) {
/** @todo clean up when Debug::toString() exists */ /** @todo clean up when Debug::toString() exists */

15
src/Magnum/SceneTools/Test/CMakeLists.txt

@ -40,6 +40,9 @@ endif()
if(MAGNUM_WITH_SCENECONVERTER AND CORRADE_TARGET_UNIX) if(MAGNUM_WITH_SCENECONVERTER AND CORRADE_TARGET_UNIX)
set(SCENECONVERTER_EXECUTABLE_FILENAME $<TARGET_FILE:magnum-sceneconverter>) set(SCENECONVERTER_EXECUTABLE_FILENAME $<TARGET_FILE:magnum-sceneconverter>)
endif() endif()
if(MAGNUM_WITH_ANYSCENECONVERTER AND NOT MAGNUM_BUILD_PLUGINS_STATIC AND NOT MAGNUM_ANYSCENECONVERTER_BUILD_STATIC)
set(ANYSCENECONVERTER_PLUGIN_FILENAME $<TARGET_FILE:AnySceneConverter>)
endif()
# First replace ${} variables, then $<> generator expressions # First replace ${} variables, then $<> generator expressions
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake
@ -69,6 +72,14 @@ corrade_add_test(SceneToolsSceneConverterImple___Test SceneConverterImplementati
SceneConverterImplementationTestFiles/info-skins.txt SceneConverterImplementationTestFiles/info-skins.txt
SceneConverterImplementationTestFiles/info-textures.txt) SceneConverterImplementationTestFiles/info-textures.txt)
target_include_directories(SceneToolsSceneConverterImple___Test PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>) target_include_directories(SceneToolsSceneConverterImple___Test PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>)
if(MAGNUM_WITH_ANYSCENECONVERTER)
if(MAGNUM_BUILD_PLUGINS_STATIC OR MAGNUM_ANYSCENECONVERTER_BUILD_STATIC)
target_link_libraries(SceneToolsSceneConverterImple___Test PRIVATE AnySceneConverter)
else()
# So the plugins get properly built when building the test
add_dependencies(SceneToolsSceneConverterImple___Test AnySceneConverter)
endif()
endif()
# Executable testing is implemented on Unix platforms only at the moment # Executable testing is implemented on Unix platforms only at the moment
if(CORRADE_TARGET_UNIX AND NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT) if(CORRADE_TARGET_UNIX AND NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT)
@ -97,6 +108,10 @@ if(CORRADE_TARGET_UNIX AND NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT)
SceneConverterTestFiles/images-3d-1x1x1.gltf SceneConverterTestFiles/images-3d-1x1x1.gltf
SceneConverterTestFiles/info-data.txt SceneConverterTestFiles/info-data.txt
SceneConverterTestFiles/info-data-ignored-output.txt SceneConverterTestFiles/info-data-ignored-output.txt
SceneConverterTestFiles/info-converter.txt
SceneConverterTestFiles/info-image-converter.txt
SceneConverterTestFiles/info-importer.txt
SceneConverterTestFiles/info-importer-ignored-input-output.txt
SceneConverterTestFiles/point.obj SceneConverterTestFiles/point.obj
SceneConverterTestFiles/quad-duplicates-fuzzy.obj SceneConverterTestFiles/quad-duplicates-fuzzy.obj
SceneConverterTestFiles/quad-duplicates.obj SceneConverterTestFiles/quad-duplicates.obj

53
src/Magnum/SceneTools/Test/SceneConverterImplementationTest.cpp

@ -44,6 +44,11 @@ namespace Magnum { namespace SceneTools { namespace Test { namespace {
struct SceneConverterImplementationTest: TestSuite::Tester { struct SceneConverterImplementationTest: TestSuite::Tester {
explicit SceneConverterImplementationTest(); explicit SceneConverterImplementationTest();
/* printPluginInfo(), printPluginConfigurationInfo() and
printImporterInfo() tested thoroughly in
Trade/Test/ImageConverterImplementationTest.cpp already */
void converterInfo();
void infoEmpty(); void infoEmpty();
void infoScenesObjects(); void infoScenesObjects();
void infoAnimations(); void infoAnimations();
@ -60,6 +65,9 @@ struct SceneConverterImplementationTest: TestSuite::Tester {
void infoError(); void infoError();
Utility::Arguments _infoArgs; Utility::Arguments _infoArgs;
/* Explicitly forbid system-wide plugin dependencies */
PluginManager::Manager<Trade::AbstractSceneConverter> _converterManager{"nonexistent"};
}; };
using namespace Containers::Literals; using namespace Containers::Literals;
@ -86,7 +94,9 @@ const struct {
}; };
SceneConverterImplementationTest::SceneConverterImplementationTest() { SceneConverterImplementationTest::SceneConverterImplementationTest() {
addTests({&SceneConverterImplementationTest::infoEmpty}); addTests({&SceneConverterImplementationTest::converterInfo,
&SceneConverterImplementationTest::infoEmpty});
addInstancedTests({&SceneConverterImplementationTest::infoScenesObjects}, addInstancedTests({&SceneConverterImplementationTest::infoScenesObjects},
Containers::arraySize(InfoScenesObjectsData)); Containers::arraySize(InfoScenesObjectsData));
@ -121,6 +131,47 @@ SceneConverterImplementationTest::SceneConverterImplementationTest() {
.addBooleanOption("info-textures") .addBooleanOption("info-textures")
.addBooleanOption("info-images") .addBooleanOption("info-images")
.addBooleanOption("bounds"); .addBooleanOption("bounds");
/* Load the plugin directly from the build tree. Otherwise it's static and
already loaded. */
#ifdef ANYSCENECONVERTER_PLUGIN_FILENAME
CORRADE_INTERNAL_ASSERT_OUTPUT(_converterManager.load(ANYSCENECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded);
#endif
/* To avoid warnings that printImageConverterInfo() / printImporterInfo()
is unused. Again, those are tested in ImageConverterImplementationTest
already. */
static_cast<void>(Trade::Implementation::printImageConverterInfo);
static_cast<void>(Trade::Implementation::printImporterInfo);
}
void SceneConverterImplementationTest::converterInfo() {
/* Check if the required plugin can be loaded. Catches also ABI and
interface mismatch errors. */
if(!(_converterManager.load("AnySceneConverter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnySceneConverter plugin can't be loaded.");
Containers::Pointer<Trade::AbstractSceneConverter> converter = _converterManager.instantiate("AnySceneConverter");
/** @todo pick a plugin that has some actual configuration */
converter->configuration().setValue("something", "is there");
/* Print to visually verify coloring */
{
Debug{} << "======================== visual color verification start =======================";
Implementation::printSceneConverterInfo(Debug::isTty() ? Debug::Flags{} : Debug::Flag::DisableColors, *converter);
Debug{} << "======================== visual color verification end =========================";
}
std::ostringstream out;
Debug redirectOutput{&out};
Implementation::printSceneConverterInfo(Debug::Flag::DisableColors, *converter);
CORRADE_COMPARE(out.str(),
"Plugin name: AnySceneConverter\n"
"Features:\n"
" ConvertMeshToFile\n"
" ConvertMultipleToFile\n"
"Configuration:\n"
" something=is there\n");
} }
void SceneConverterImplementationTest::infoEmpty() { void SceneConverterImplementationTest::infoEmpty() {

38
src/Magnum/SceneTools/Test/SceneConverterTest.cpp

@ -57,21 +57,47 @@ const struct {
const char* name; const char* name;
Containers::Array<Containers::String> args; Containers::Array<Containers::String> args;
const char* requiresImporter; const char* requiresImporter;
const char* requiresConverter;
const char* requiresImageConverter;
const char* expected; const char* expected;
} InfoData[]{ } InfoData[]{
{"importer", Containers::array<Containers::String>({
"--info-importer", "-i", "someOption=yes"}),
"AnySceneImporter", nullptr, nullptr,
"info-importer.txt"},
{"converter", Containers::array<Containers::String>({
"-C", "AnySceneConverter", "--info-converter", "-c", "someOption=yes"}),
nullptr, "AnySceneConverter", nullptr,
"info-converter.txt"},
{"converter, implicit", Containers::array<Containers::String>({
"--info-converter", "-c", "someOption=yes"}),
nullptr, "AnySceneConverter", nullptr,
"info-converter.txt"},
{"image converter", Containers::array<Containers::String>({
"-P", "AnyImageConverter", "--info-image-converter", "-p", "someOption=yes"}),
nullptr, nullptr, "AnyImageConverter",
"info-image-converter.txt"},
{"image converter, implicit", Containers::array<Containers::String>({
"--info-image-converter", "-p", "someOption=yes"}),
nullptr, nullptr, "AnyImageConverter",
"info-image-converter.txt"},
{"importer, ignored input and output", Containers::array<Containers::String>({
"--info-importer", "input.obj", "output.ply"}),
"AnySceneImporter", nullptr, nullptr,
"info-importer-ignored-input-output.txt"},
{"data", Containers::array<Containers::String>({ {"data", Containers::array<Containers::String>({
"-I", "ObjImporter", "--info", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj")}), "-I", "ObjImporter", "--info", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj")}),
"ObjImporter", "ObjImporter", nullptr, nullptr,
"info-data.txt"}, "info-data.txt"},
{"data, map", Containers::array<Containers::String>({ {"data, map", Containers::array<Containers::String>({
"--map", "-I", "ObjImporter", "--info", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj")}), "--map", "-I", "ObjImporter", "--info", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj")}),
"ObjImporter", "ObjImporter", nullptr, nullptr,
/** @todo change to something else once we have a plugin that can /** @todo change to something else once we have a plugin that can
zero-copy pass the imported data */ zero-copy pass the imported data */
"info-data.txt"}, "info-data.txt"},
{"data, ignored output file", Containers::array<Containers::String>({ {"data, ignored output file", Containers::array<Containers::String>({
"-I", "ObjImporter", "--info", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj"), "whatever.ply"}), "-I", "ObjImporter", "--info", Utility::Path::join(SCENETOOLS_TEST_DIR, "SceneConverterTestFiles/point.obj"), "whatever.ply"}),
"ObjImporter", "ObjImporter", nullptr, nullptr,
"info-data-ignored-output.txt"} "info-data-ignored-output.txt"}
}; };
@ -783,8 +809,14 @@ void SceneConverterTest::info() {
/* Check if required plugins can be loaded. Catches also ABI and interface /* Check if required plugins can be loaded. Catches also ABI and interface
mismatch errors. */ mismatch errors. */
PluginManager::Manager<Trade::AbstractImporter> importerManager{MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR}; PluginManager::Manager<Trade::AbstractImporter> importerManager{MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR};
PluginManager::Manager<Trade::AbstractImageConverter> imageConverterManager{MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR};
PluginManager::Manager<Trade::AbstractSceneConverter> converterManager{MAGNUM_PLUGINS_SCENECONVERTER_INSTALL_DIR};
if(data.requiresImporter && !(importerManager.load(data.requiresImporter) & PluginManager::LoadState::Loaded)) if(data.requiresImporter && !(importerManager.load(data.requiresImporter) & PluginManager::LoadState::Loaded))
CORRADE_SKIP(data.requiresImporter << "plugin can't be loaded."); CORRADE_SKIP(data.requiresImporter << "plugin can't be loaded.");
if(data.requiresConverter && !(converterManager.load(data.requiresConverter) & PluginManager::LoadState::Loaded))
CORRADE_SKIP(data.requiresConverter << "plugin can't be loaded.");
if(data.requiresImageConverter && !(imageConverterManager.load(data.requiresImageConverter) & PluginManager::LoadState::Loaded))
CORRADE_SKIP(data.requiresImageConverter << "plugin can't be loaded.");
CORRADE_VERIFY(true); /* capture correct function name */ CORRADE_VERIFY(true); /* capture correct function name */

6
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-converter.txt

@ -0,0 +1,6 @@
Plugin name: AnySceneConverter
Features:
ConvertMeshToFile
ConvertMultipleToFile
Configuration:
someOption=yes

11
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-image-converter.txt

@ -0,0 +1,11 @@
Plugin name: AnyImageConverter
Features:
Convert1DToFile
Convert2DToFile
Convert3DToFile
ConvertCompressed1DToFile
ConvertCompressed2DToFile
ConvertCompressed3DToFile
Levels
Configuration:
someOption=yes

5
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-importer-ignored-input-output.txt

@ -0,0 +1,5 @@
Ignoring input file for --info: input.obj
Ignoring output file for --info: output.ply
Plugin name: AnySceneImporter
Features:
FileCallback

5
src/Magnum/SceneTools/Test/SceneConverterTestFiles/info-importer.txt

@ -0,0 +1,5 @@
Plugin name: AnySceneImporter
Features:
FileCallback
Configuration:
someOption=yes

1
src/Magnum/SceneTools/Test/configure.h.cmake

@ -26,6 +26,7 @@
#define SCENETOOLS_TEST_DIR "${SCENETOOLS_TEST_DIR}" #define SCENETOOLS_TEST_DIR "${SCENETOOLS_TEST_DIR}"
#define SCENETOOLS_TEST_OUTPUT_DIR "${SCENETOOLS_TEST_OUTPUT_DIR}" #define SCENETOOLS_TEST_OUTPUT_DIR "${SCENETOOLS_TEST_OUTPUT_DIR}"
#cmakedefine SCENECONVERTER_EXECUTABLE_FILENAME "${SCENECONVERTER_EXECUTABLE_FILENAME}" #cmakedefine SCENECONVERTER_EXECUTABLE_FILENAME "${SCENECONVERTER_EXECUTABLE_FILENAME}"
#cmakedefine ANYSCENECONVERTER_PLUGIN_FILENAME "${ANYSCENECONVERTER_PLUGIN_FILENAME}"
#ifdef CORRADE_TARGET_WINDOWS #ifdef CORRADE_TARGET_WINDOWS
#ifdef CORRADE_IS_DEBUG_BUILD #ifdef CORRADE_IS_DEBUG_BUILD

116
src/Magnum/SceneTools/sceneconverter.cpp

@ -126,7 +126,8 @@ magnum-sceneconverter [-h|--help] [-I|--importer PLUGIN]
[-c|--converter-options key=val,key2=val2,]... [-c|--converter-options key=val,key2=val2,]...
[-p|--image-converter-options key=val,key2=val2,]... [-p|--image-converter-options key=val,key2=val2,]...
[-m|--mesh-converter-options key=val,key2=val2,]... [-m|--mesh-converter-options key=val,key2=val2,]...
[--mesh ID] [--mesh-level INDEX] [--concatenate-meshes] [--info-animations] [--mesh ID] [--mesh-level INDEX] [--concatenate-meshes] [--info-importer]
[--info-converter] [--info-image-converter] [--info-animations]
[--info-images] [--info-lights] [--info-cameras] [--info-materials] [--info-images] [--info-lights] [--info-cameras] [--info-materials]
[--info-meshes] [--info-objects] [--info-scenes] [--info-skins] [--info-meshes] [--info-objects] [--info-scenes] [--info-skins]
[--info-textures] [--info] [--color on|4bit|off|auto] [--bounds] [--info-textures] [--info] [--color on|4bit|off|auto] [--bounds]
@ -169,6 +170,11 @@ Arguments:
- `--mesh-level LEVEL` --- level to select for single-mesh conversion - `--mesh-level LEVEL` --- level to select for single-mesh conversion
- `--concatenate-meshes` -- flatten mesh hierarchy and concatenate them all - `--concatenate-meshes` -- flatten mesh hierarchy and concatenate them all
together @m_class{m-label m-warning} **experimental** together @m_class{m-label m-warning} **experimental**
- `--info-importer` --- print info about the importer plugin and exit
- `--info-converter` --- print info about the scene or mesh converter plugin
and exit
- `--info-image-converter` --- print info about the image converter plugin
and exit
- `--info-animations` --- print into about animations in the input file and - `--info-animations` --- print into about animations in the input file and
exit exit
- `--info-images` --- print into about images in the input file and exit - `--info-images` --- print into about images in the input file and exit
@ -182,18 +188,25 @@ Arguments:
- `--info-skins` --- print into about skins in the input file and exit - `--info-skins` --- print into about skins in the input file and exit
- `--info-textures` --- print into about textures in the input file and exit - `--info-textures` --- print into about textures in the input file and exit
- `--info` --- print info about everything in the input file and exit, same - `--info` --- print info about everything in the input file and exit, same
as specifying all other `--info-*` options together as specifying all other data-related `--info-*` options together
- `--color` --- colored output for `--info` (default: `auto`) - `--color` --- colored output for `--info` (default: `auto`)
- `--bounds` --- show bounds of known attributes in `--info` output - `--bounds` --- show bounds of known attributes in `--info` output
- `-v`, `--verbose` --- verbose output from importer and converter plugins - `-v`, `--verbose` --- verbose output from importer and converter plugins
- `--profile` --- measure import and conversion time - `--profile` --- measure import and conversion time
If any of the `--info-*` options are given, the utility will print information If any of the `--info-importer`, `--info-converter` or `--info-image-converter`
about given data present in the file. In this case no conversion is done and options are given, the utility will print information about given plugin
output file doesn't need to be specified. In case one data references another specified via the `-I`, `-C` or `-P` option, including its configuration
and both `--info-*` options are specified, the output will also list reference options potentially overriden with `-i`, `-c` or `-p`. In this case no file is
count (for example, `--info-scenes` together with `--info-meshes` will print read and no conversion is done and neither the input nor the output file needs
how many objects reference given mesh). to be specified.
If any of the other `--info-*` options are given, the utility will print
information about given data. In this case the input file is read but no
conversion is done and the output file doesn't need to be specified. In case
one data references another and both `--info-*` options are specified, the
output will also list reference count (for example, `--info-scenes` together
with `--info-meshes` will print how many objects reference given mesh).
The `-i`, `-c` and `-m` arguments accept a comma-separated list of key/value The `-i`, `-c` and `-m` arguments accept a comma-separated list of key/value
pairs to set in the importer / converter plugin configuration. If the `=` pairs to set in the importer / converter plugin configuration. If the `=`
@ -239,7 +252,13 @@ using namespace Containers::Literals;
namespace { namespace {
bool isInfoRequested(const Utility::Arguments& args) { bool isPluginInfoRequested(const Utility::Arguments& args) {
return args.isSet("info-importer") ||
args.isSet("info-converter") ||
args.isSet("info-image-converter");
}
bool isDataInfoRequested(const Utility::Arguments& args) {
return args.isSet("info-animations") || return args.isSet("info-animations") ||
args.isSet("info-images") || args.isSet("info-images") ||
args.isSet("info-lights") || args.isSet("info-lights") ||
@ -333,6 +352,9 @@ int main(int argc, char** argv) {
.addOption("mesh").setHelp("mesh", "convert just a single mesh instead of the whole scene, ignored if --concatenate-meshes is specified", "ID") .addOption("mesh").setHelp("mesh", "convert just a single mesh instead of the whole scene, ignored if --concatenate-meshes is specified", "ID")
.addOption("mesh-level").setHelp("mesh-level", "level to select for single-mesh conversion", "index") .addOption("mesh-level").setHelp("mesh-level", "level to select for single-mesh conversion", "index")
.addBooleanOption("concatenate-meshes").setHelp("concatenate-meshes", "flatten mesh hierarchy and concatenate them all together") .addBooleanOption("concatenate-meshes").setHelp("concatenate-meshes", "flatten mesh hierarchy and concatenate them all together")
.addBooleanOption("info-importer").setHelp("info-importer", "print info about the importer plugin and exit")
.addBooleanOption("info-converter").setHelp("info-converter", "print info about the scene or mesh converter plugin and exit")
.addBooleanOption("info-image-converter").setHelp("info-image-converter", "print info about the image converter plugin and exit")
.addBooleanOption("info-animations").setHelp("info-animations", "print info about animations in the input file and exit") .addBooleanOption("info-animations").setHelp("info-animations", "print info about animations in the input file and exit")
.addBooleanOption("info-images").setHelp("info-images", "print info about images in the input file and exit") .addBooleanOption("info-images").setHelp("info-images", "print info about images in the input file and exit")
.addBooleanOption("info-lights").setHelp("info-lights", "print info about images in the input file and exit") .addBooleanOption("info-lights").setHelp("info-lights", "print info about images in the input file and exit")
@ -343,27 +365,39 @@ int main(int argc, char** argv) {
.addBooleanOption("info-scenes").setHelp("info-scenes", "print info about scenes in the input file and exit") .addBooleanOption("info-scenes").setHelp("info-scenes", "print info about scenes in the input file and exit")
.addBooleanOption("info-skins").setHelp("info-skins", "print info about skins in the input file and exit") .addBooleanOption("info-skins").setHelp("info-skins", "print info about skins in the input file and exit")
.addBooleanOption("info-textures").setHelp("info-textures", "print info about textures in the input file and exit") .addBooleanOption("info-textures").setHelp("info-textures", "print info about textures in the input file and exit")
.addBooleanOption("info").setHelp("info", "print info about everything in the input file and exit, same as specifying all other --info-* options together") .addBooleanOption("info").setHelp("info", "print info about everything in the input file and exit, same as specifying all other data-related --info-* options together")
.addOption("color", "auto").setHelp("color", "colored output for --info", "on|4bit|off|auto") .addOption("color", "auto").setHelp("color", "colored output for --info", "on|4bit|off|auto")
.addBooleanOption("bounds").setHelp("bounds", "show bounds of known attributes in --info output") .addBooleanOption("bounds").setHelp("bounds", "show bounds of known attributes in --info output")
.addBooleanOption('v', "verbose").setHelp("verbose", "verbose output from importer and converter plugins") .addBooleanOption('v', "verbose").setHelp("verbose", "verbose output from importer and converter plugins")
.addBooleanOption("profile").setHelp("profile", "measure import and conversion time") .addBooleanOption("profile").setHelp("profile", "measure import and conversion time")
.setParseErrorCallback([](const Utility::Arguments& args, Utility::Arguments::ParseError error, const std::string& key) { .setParseErrorCallback([](const Utility::Arguments& args, Utility::Arguments::ParseError error, const std::string& key) {
/* If --info is passed, we don't need the output argument */ /* If --info for plugins is passed, we don't need the input */
if(error == Utility::Arguments::ParseError::MissingArgument && if(error == Utility::Arguments::ParseError::MissingArgument &&
key == "output" && isInfoRequested(args)) return true; key == "input" && isPluginInfoRequested(args))
return true;
/* If --info for plugins or data is passed, we don't need the
output argument */
if(error == Utility::Arguments::ParseError::MissingArgument &&
key == "output" && (isPluginInfoRequested(args) || isDataInfoRequested(args)))
return true;
/* Handle all other errors as usual */ /* Handle all other errors as usual */
return false; return false;
}) })
.setGlobalHelp(R"(Converts scenes of different formats. .setGlobalHelp(R"(Converts scenes of different formats.
If any of the --info-* options are given, the utility will print information If any of the --info-importer, --info-converter or --info-image-converter
about given data present in the file. In this case no conversion is done and options are given, the utility will print information about given plugin
output file doesn't need to be specified. In case one data references another specified via the -I, -C or -P option. In this case no file is read and no
and both --info-* options are specified, the output will also list reference conversion is done and neither the input nor the output file needs to be
count (for example, --info-scenes together with --info-meshes will print how specified.
many objects reference given mesh).
If any of the other --info-* options are given, the utility will print
information about given data. In this case the input file is read but no
conversion is done and the output file doesn't need to be specified. In case
one data references another and both --info-* options are specified, the output
will also list reference count (for example, --info-scenes together with
--info-meshes will print how many objects reference given mesh).
The -i, -c and -m arguments accept a comma-separated list of key/value The -i, -c and -m arguments accept a comma-separated list of key/value
pairs to set in the importer / converter plugin configuration. If the = pairs to set in the importer / converter plugin configuration. If the =
@ -421,11 +455,17 @@ well, the IDs reference attributes of the first mesh.)")
} }
/* Generic checks */ /* Generic checks */
if(args.value<Containers::StringView>("output")) { if(args.value<Containers::StringView>("input")) {
/* Not an error in this case, it should be possible to just append /* Not an error in this case, it should be possible to just append
--info* to existing command line without having to remove anything. --info* to existing command line without having to remove anything.
But print a warning at least, it could also be a mistyped option. */ But print a warning at least, it could also be a mistyped option. */
if(isInfoRequested(args)) if(isPluginInfoRequested(args))
Warning{} << "Ignoring input file for --info:" << args.value<Containers::StringView>("input");
}
if(args.value<Containers::StringView>("output")) {
/* Same as above, it should be possible to just append --info* to
existing command line */
if(isPluginInfoRequested(args) || isDataInfoRequested(args))
Warning{} << "Ignoring output file for --info:" << args.value<Containers::StringView>("output"); Warning{} << "Ignoring output file for --info:" << args.value<Containers::StringView>("output");
} }
if(args.isSet("concatenate-meshes") && args.value<Containers::StringView>("mesh")) { if(args.isSet("concatenate-meshes") && args.value<Containers::StringView>("mesh")) {
@ -471,6 +511,40 @@ well, the IDs reference attributes of the first mesh.)")
if(args.isSet("verbose")) importer->addFlags(Trade::ImporterFlag::Verbose); if(args.isSet("verbose")) importer->addFlags(Trade::ImporterFlag::Verbose);
Implementation::setOptions(*importer, "AnySceneImporter", args.value("importer-options")); Implementation::setOptions(*importer, "AnySceneImporter", args.value("importer-options"));
/* Print plugin info, if requested */
if(args.isSet("info-importer")) {
Trade::Implementation::printImporterInfo(useColor, *importer);
return 0;
}
if(args.isSet("info-converter")) {
Containers::Pointer<Trade::AbstractSceneConverter> converter = converterManager.loadAndInstantiate(args.arrayValueCount("converter") ? args.arrayValue("converter", 0) : "AnySceneConverter");
if(!converter) {
Debug{} << "Available converter plugins:" << ", "_s.join(converterManager.aliasList());
return 1;
}
/* Set options, if passed */
if(args.isSet("verbose")) converter->addFlags(Trade::SceneConverterFlag::Verbose);
if(args.arrayValueCount("converter-options"))
Implementation::setOptions(*converter, "AnySceneConverter", args.arrayValue("converter-options", 0));
SceneTools::Implementation::printSceneConverterInfo(useColor, *converter);
return 0;
}
if(args.isSet("info-image-converter")) {
Containers::Pointer<Trade::AbstractImageConverter> converter = imageConverterManager.loadAndInstantiate(args.arrayValueCount("image-converter") ? args.arrayValue("image-converter", 0) : "AnyImageConverter");
if(!converter) {
Debug{} << "Available image converter plugins:" << ", "_s.join(imageConverterManager.aliasList());
return 1;
}
/* Set options, if passed */
if(args.isSet("verbose")) converter->addFlags(Trade::ImageConverterFlag::Verbose);
if(args.arrayValueCount("image-converter-options"))
Implementation::setOptions(*converter, "AnyImageConverter", args.arrayValue("image-converter-options", 0));
Trade::Implementation::printImageConverterInfo(useColor, *converter);
return 0;
}
/* Wow, C++, you suck. This implicitly initializes to random shit?! /* Wow, C++, you suck. This implicitly initializes to random shit?!
Also, because of addSupportedImporterContents() it's not really possible Also, because of addSupportedImporterContents() it's not really possible
@ -499,7 +573,7 @@ well, the IDs reference attributes of the first mesh.)")
} }
/* Print file info, if requested */ /* Print file info, if requested */
if(isInfoRequested(args)) { if(isDataInfoRequested(args)) {
const bool error = SceneTools::Implementation::printInfo(useColor, useColor24, args, *importer, importConversionTime); const bool error = SceneTools::Implementation::printInfo(useColor, useColor24, args, *importer, importConversionTime);
if(args.isSet("profile")) { if(args.isSet("profile")) {

103
src/Magnum/Trade/Implementation/converterUtilities.h

@ -26,13 +26,21 @@
*/ */
#include <chrono> #include <chrono>
#include <sstream> /** @todo remove when Debug is stream-free */
#include <Corrade/Containers/GrowableArray.h> #include <Corrade/Containers/GrowableArray.h>
#include <Corrade/Containers/Optional.h> #include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Pair.h>
#include <Corrade/Containers/Reference.h>
#include <Corrade/Containers/String.h> #include <Corrade/Containers/String.h>
#include <Corrade/PluginManager/PluginMetadata.h>
#include <Corrade/Utility/Format.h> #include <Corrade/Utility/Format.h>
#include <Corrade/Utility/FormatStl.h> /** @todo drop once String::replaceAll() has a StringView overload */
#include <Corrade/Utility/String.h>
#include <Corrade/Utility/ConfigurationGroup.h>
#include "Magnum/PixelFormat.h" #include "Magnum/PixelFormat.h"
#include "Magnum/Trade/AbstractImporter.h" #include "Magnum/Trade/AbstractImporter.h"
#include "Magnum/Trade/AbstractImageConverter.h"
#include "Magnum/Trade/ImageData.h" #include "Magnum/Trade/ImageData.h"
namespace Magnum { namespace Trade { namespace Implementation { namespace Magnum { namespace Trade { namespace Implementation {
@ -41,6 +49,101 @@ namespace Magnum { namespace Trade { namespace Implementation {
particular magnum-imageconverter, magnum-sceneconverter and their tests */ particular magnum-imageconverter, magnum-sceneconverter and their tests */
namespace { namespace {
/** @todo move these to Magnum/Implementation/converterUtilities.h once also
shaderconverter / fontconverter / etc implements --info-converter etc */
template<class T> void printPluginInfo(const Debug::Flags useColor, const T& plugin) {
const PluginManager::PluginMetadata* metadata = plugin.metadata();
CORRADE_INTERNAL_ASSERT(metadata);
Debug d{useColor};
d << Debug::boldColor(Debug::Color::Default) << "Plugin name:" << Debug::boldColor(Debug::Color::Yellow) << metadata->name() << Debug::resetColor;
const std::vector<std::string> aliases = metadata->provides();
if(!aliases.empty()) {
d << Debug::newline << Debug::boldColor(Debug::Color::Default) << "Aliases:" << Debug::resetColor;
for(const std::string& alias: aliases) {
d << Debug::newline << " ";
if(alias == plugin.plugin())
d << Debug::color(Debug::Color::Yellow);
d << alias;
if(alias == plugin.plugin())
d << Debug::resetColor;
}
}
/* Ugly, eh? */
std::ostringstream out;
Debug{&out, Debug::Flag::NoNewlineAtTheEnd} << Debug::packed << plugin.features();
d << Debug::newline << Debug::boldColor(Debug::Color::Default) << "Features:" << Debug::color(Debug::Color::Cyan) << Debug::newline << " " << Utility::String::replaceAll(out.str(), "|", "\n ") << Debug::resetColor;
}
void printPluginConfigurationInfo(Debug& d, const Utility::ConfigurationGroup& configuration, const Containers::StringView prefix) {
using namespace Containers::Literals;
for(Containers::Pair<Containers::StringView, Containers::StringView> i: configuration.valuesComments()) {
if(i.first()) {
d << Debug::newline << " " << Debug::boldColor(Debug::Color::Blue) << i.first() << Debug::nospace << Debug::color(Debug::Color::Blue) << "=" << Debug::nospace << Debug::color(Debug::Color::Red);
/* Print the value wrapped in quotes if it contains spaces, indent
also all newlines */
if(i.second().contains('\n'))
/** @todo replaceAll() without std::string */
d << Utility::format("\"\"\"\n {}\n \"\"\"", Utility::String::replaceAll(i.second(), "\n", "\n "));
/** @todo less wasteful API for checking leading/trailing zeros? */
else if(i.second().trimmed() != i.second())
d << Utility::format("\"{}\"", i.second());
else
d << i.second();
d << Debug::resetColor;
} else {
/* Configuration contents are delimited by these markers in order
to include them in Doxygen-generated docs. If we reach them,
it's the end of the (public) configuration values. Don't print
them, don't print even the last newline, and exit. */
if(i.second() == "# [configuration_]"_s) {
return;
}
/* Print leading space only if there's actually something */
d << Debug::newline;
if(i.second()) d << " " << Debug::boldColor(Debug::Color::Black) << i.second() << Debug::resetColor;
}
}
for(Containers::Pair<Containers::StringView, Containers::Reference<const Utility::ConfigurationGroup>> i: configuration.groups()) {
const Containers::String name = prefix ? "/"_s.join({prefix, i.first()}) : Containers::String{i.first()};
d << Debug::newline << " " << Debug::color(Debug::Color::Blue) << "[" << Debug::nospace << Debug::boldColor(Debug::Color::Blue) << name << Debug::color(Debug::Color::Blue) << Debug::nospace << "]" << Debug::resetColor;
printPluginConfigurationInfo(d, i.second(), name);
}
}
void printPluginConfigurationInfo(const Debug::Flags useColor, const PluginManager::AbstractPlugin& plugin) {
const Utility::ConfigurationGroup& configuration = plugin.configuration();
if(configuration.isEmpty()) return;
Debug d{useColor};
d << Debug::boldColor(Debug::Color::Default) << "Configuration:" << Debug::resetColor;
printPluginConfigurationInfo(d, configuration, {});
}
void printImporterInfo(const Debug::Flags useColor, const Trade::AbstractImporter& importer) {
printPluginInfo(useColor, importer);
printPluginConfigurationInfo(useColor, importer);
}
void printImageConverterInfo(const Debug::Flags useColor, const Trade::AbstractImageConverter& converter) {
printPluginInfo(useColor, converter);
Debug d{useColor|Debug::Flag::NoNewlineAtTheEnd};
if(const Containers::String extension = converter.extension())
d << Debug::boldColor(Debug::Color::Default) << "File extension:" << Debug::resetColor << extension << Debug::newline;
if(const Containers::String mimeType = converter.mimeType())
d << Debug::boldColor(Debug::Color::Default) << "MIME type:" << Debug::resetColor << mimeType << Debug::newline;
printPluginConfigurationInfo(useColor, converter);
}
struct Duration { struct Duration {
explicit Duration(std::chrono::high_resolution_clock::duration& output): _output(output), _t{std::chrono::high_resolution_clock::now()} {} explicit Duration(std::chrono::high_resolution_clock::duration& output): _output(output), _t{std::chrono::high_resolution_clock::now()} {}

38
src/Magnum/Trade/Test/CMakeLists.txt

@ -40,6 +40,17 @@ endif()
if(MAGNUM_WITH_IMAGECONVERTER AND CORRADE_TARGET_UNIX) if(MAGNUM_WITH_IMAGECONVERTER AND CORRADE_TARGET_UNIX)
set(IMAGECONVERTER_EXECUTABLE_FILENAME $<TARGET_FILE:magnum-imageconverter>) set(IMAGECONVERTER_EXECUTABLE_FILENAME $<TARGET_FILE:magnum-imageconverter>)
endif() endif()
if(NOT MAGNUM_BUILD_PLUGINS_STATIC)
if(MAGNUM_WITH_ANYIMAGEIMPORTER AND NOT MAGNUM_ANYIMAGEIMPORTER_BUILD_STATIC)
set(ANYIMAGEIMPORTER_PLUGIN_FILENAME $<TARGET_FILE:AnyImageImporter>)
endif()
if(MAGNUM_WITH_ANYIMAGECONVERTER AND NOT MAGNUM_ANYIMAGECONVERTER_BUILD_STATIC)
set(ANYIMAGECONVERTER_PLUGIN_FILENAME $<TARGET_FILE:AnyImageConverter>)
endif()
if(MAGNUM_WITH_TGAIMAGECONVERTER AND NOT MAGNUM_TGAIMAGECONVERTER_BUILD_STATIC)
set(TGAIMAGECONVERTER_PLUGIN_FILENAME $<TARGET_FILE:TgaImageConverter>)
endif()
endif()
# First replace ${} variables, then $<> generator expressions # First replace ${} variables, then $<> generator expressions
configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake configure_file(${CMAKE_CURRENT_SOURCE_DIR}/configure.h.cmake
@ -69,6 +80,30 @@ corrade_add_test(TradeImageConverterImplementa___Test ImageConverterImplementati
FILES FILES
ImageConverterImplementationTestFiles/info.txt) ImageConverterImplementationTestFiles/info.txt)
target_include_directories(TradeImageConverterImplementa___Test PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>) target_include_directories(TradeImageConverterImplementa___Test PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>)
if(MAGNUM_WITH_ANYIMAGEIMPORTER)
if(MAGNUM_BUILD_PLUGINS_STATIC OR MAGNUM_ANYIMAGEIMPORTER_BUILD_STATIC)
target_link_libraries(TradeImageConverterImplementa___Test PRIVATE AnyImageImporter)
else()
# So the plugins get properly built when building the test
add_dependencies(TradeImageConverterImplementa___Test AnyImageImporter)
endif()
endif()
if(MAGNUM_WITH_ANYIMAGECONVERTER)
if(MAGNUM_BUILD_PLUGINS_STATIC OR MAGNUM_ANYIMAGECONVERTER_BUILD_STATIC)
target_link_libraries(TradeImageConverterImplementa___Test PRIVATE AnyImageConverter)
else()
# So the plugins get properly built when building the test
add_dependencies(TradeImageConverterImplementa___Test AnyImageConverter)
endif()
endif()
if(MAGNUM_WITH_TGAIMAGECONVERTER)
if(MAGNUM_BUILD_PLUGINS_STATIC OR MAGNUM_TGAIMAGECONVERTER_BUILD_STATIC)
target_link_libraries(TradeImageConverterImplementa___Test PRIVATE TgaImageConverter)
else()
# So the plugins get properly built when building the test
add_dependencies(TradeImageConverterImplementa___Test TgaImageConverter)
endif()
endif()
# Executable testing is implemented on Unix platforms only at the moment # Executable testing is implemented on Unix platforms only at the moment
if(CORRADE_TARGET_UNIX AND NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT) if(CORRADE_TARGET_UNIX AND NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT)
@ -81,6 +116,9 @@ if(CORRADE_TARGET_UNIX AND NOT CORRADE_PLUGINMANAGER_NO_DYNAMIC_PLUGIN_SUPPORT)
FILES FILES
ImageConverterTestFiles/info-data.txt ImageConverterTestFiles/info-data.txt
ImageConverterTestFiles/info-data-ignored-output.txt ImageConverterTestFiles/info-data-ignored-output.txt
ImageConverterTestFiles/info-converter.txt
ImageConverterTestFiles/info-importer.txt
ImageConverterTestFiles/info-importer-ignored-input-output.txt
ImageConverterTestFiles/file.tga) ImageConverterTestFiles/file.tga)
target_include_directories(TradeImageConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>) target_include_directories(TradeImageConverterTest PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>)
if(MAGNUM_WITH_IMAGECONVERTER) if(MAGNUM_WITH_IMAGECONVERTER)

315
src/Magnum/Trade/Test/ImageConverterImplementationTest.cpp

@ -27,6 +27,7 @@
#include <Corrade/Containers/StringStl.h> /** @todo remove once Debug is stream-free */ #include <Corrade/Containers/StringStl.h> /** @todo remove once Debug is stream-free */
#include <Corrade/TestSuite/Tester.h> #include <Corrade/TestSuite/Tester.h>
#include <Corrade/TestSuite/Compare/StringToFile.h> #include <Corrade/TestSuite/Compare/StringToFile.h>
#include <Corrade/Utility/Configuration.h>
#include <Corrade/Utility/DebugStl.h> /** @todo remove once Debug is stream-free */ #include <Corrade/Utility/DebugStl.h> /** @todo remove once Debug is stream-free */
#include <Corrade/Utility/Path.h> #include <Corrade/Utility/Path.h>
@ -39,13 +40,325 @@ namespace Magnum { namespace Trade { namespace Test { namespace {
struct ImageConverterImplementationTest: TestSuite::Tester { struct ImageConverterImplementationTest: TestSuite::Tester {
explicit ImageConverterImplementationTest(); explicit ImageConverterImplementationTest();
void pluginInfo();
void pluginInfoAliases();
void pluginConfigurationInfoEmpty();
void pluginConfigurationInfo();
void pluginConfigurationInfoDoxygenDelimiter();
void importerInfo();
void converterInfo();
void converterInfoExtensionMimeType();
void info(); void info();
void infoError(); void infoError();
/* Explicitly forbid system-wide plugin dependencies */
PluginManager::Manager<Trade::AbstractImporter> _importerManager{"nonexistent"};
PluginManager::Manager<Trade::AbstractImageConverter> _converterManager{"nonexistent"};
}; };
ImageConverterImplementationTest::ImageConverterImplementationTest() { ImageConverterImplementationTest::ImageConverterImplementationTest() {
addTests({&ImageConverterImplementationTest::info, addTests({&ImageConverterImplementationTest::pluginInfo,
&ImageConverterImplementationTest::pluginInfoAliases,
&ImageConverterImplementationTest::pluginConfigurationInfoEmpty,
&ImageConverterImplementationTest::pluginConfigurationInfo,
&ImageConverterImplementationTest::pluginConfigurationInfoDoxygenDelimiter,
&ImageConverterImplementationTest::importerInfo,
&ImageConverterImplementationTest::converterInfo,
&ImageConverterImplementationTest::converterInfoExtensionMimeType,
&ImageConverterImplementationTest::info,
&ImageConverterImplementationTest::infoError}); &ImageConverterImplementationTest::infoError});
/* Load the plugin directly from the build tree. Otherwise it's static and
already loaded. */
#ifdef ANYIMAGEIMPORTER_PLUGIN_FILENAME
CORRADE_INTERNAL_ASSERT_OUTPUT(_importerManager.load(ANYIMAGEIMPORTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded);
#endif
#ifdef ANYIMAGECONVERTER_PLUGIN_FILENAME
CORRADE_INTERNAL_ASSERT_OUTPUT(_converterManager.load(ANYIMAGECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded);
#endif
#ifdef TGAIMAGECONVERTER_PLUGIN_FILENAME
CORRADE_INTERNAL_ASSERT_OUTPUT(_converterManager.load(TGAIMAGECONVERTER_PLUGIN_FILENAME) & PluginManager::LoadState::Loaded);
#endif
}
void ImageConverterImplementationTest::pluginInfo() {
/* Check if the required plugin can be loaded. Catches also ABI and
interface mismatch errors. */
if(!(_converterManager.load("AnyImageConverter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageConverter plugin can't be loaded.");
Containers::Pointer<Trade::AbstractImageConverter> converter = _converterManager.instantiate("AnyImageConverter");
/* Print to visually verify coloring */
{
Debug{} << "======================== visual color verification start =======================";
Implementation::printPluginInfo(Debug::isTty() ? Debug::Flags{} : Debug::Flag::DisableColors, *converter);
Debug{} << "======================== visual color verification end =========================";
}
std::ostringstream out;
Debug redirectOutput{&out};
Implementation::printPluginInfo(Debug::Flag::DisableColors, *converter);
CORRADE_COMPARE(out.str(),
"Plugin name: AnyImageConverter\n"
"Features:\n"
" Convert1DToFile\n"
" Convert2DToFile\n"
" Convert3DToFile\n"
" ConvertCompressed1DToFile\n"
" ConvertCompressed2DToFile\n"
" ConvertCompressed3DToFile\n"
" Levels\n");
}
void ImageConverterImplementationTest::pluginInfoAliases() {
PluginManager::Manager<Trade::AbstractImporter> importerManager{MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR};
/* Check if the required plugin can be loaded. Catches also ABI and
interface mismatch errors. */
if(!(importerManager.load("StbImageImporter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("StbImageImporter plugin can't be loaded.");
/* Loading under an alias to verify that it's highlighted. Make
StbImageImporter *the* plugin to load PPMs, so it's not replaced by e.g.
DevIlImageImporter. */
importerManager.setPreferredPlugins("PpmImporter", {"StbImageImporter"});
Containers::Pointer<Trade::AbstractImporter> importer = importerManager.instantiate("PpmImporter");
/* Print to visually verify coloring */
{
Debug{} << "======================== visual color verification start =======================";
Implementation::printPluginInfo(Debug::isTty() ? Debug::Flags{} : Debug::Flag::DisableColors, *importer);
Debug{} << "======================== visual color verification end =========================";
}
std::ostringstream out;
Debug redirectOutput{&out};
Implementation::printPluginInfo(Debug::Flag::DisableColors, *importer);
CORRADE_COMPARE(out.str(),
"Plugin name: StbImageImporter\n"
"Aliases:\n"
" BmpImporter\n"
" GifImporter\n"
" HdrImporter\n"
" JpegImporter\n"
" PgmImporter\n"
" PicImporter\n"
" PngImporter\n"
" PpmImporter\n"
" PsdImporter\n"
" TgaImporter\n"
"Features:\n"
" OpenData\n");
}
void ImageConverterImplementationTest::pluginConfigurationInfoEmpty() {
struct: Trade::AbstractImporter {
Trade::ImporterFeatures doFeatures() const override {
return Trade::ImporterFeature::FileCallback|
Trade::ImporterFeature::OpenState;
}
bool doIsOpened() const override { return false; }
void doClose() override {}
} importer;
std::ostringstream out;
Debug redirectOutput{&out};
Implementation::printPluginConfigurationInfo(Debug::Flag::DisableColors, importer);
CORRADE_COMPARE(out.str(), "");
}
void ImageConverterImplementationTest::pluginConfigurationInfo() {
struct: Trade::AbstractImporter {
Trade::ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return false; }
void doClose() override {}
} importer;
std::stringstream in;
in << R"([configuration]
# A comment
; Another
value=yes
another=42
# Empty lines should not have trailing whitespace
[configuration/group]
spaces=" YES "
newlines="""
A
L
S
O
"""
[configuration/group/subgroup]
subvalue=35
# Another instance of the same group
[configuration/group]
true=false
)";
Utility::Configuration conf{in};
importer.configuration() = Utility::ConfigurationGroup{*conf.group("configuration")};
/* Print to visually verify coloring */
{
Debug{} << "======================== visual color verification start =======================";
Implementation::printPluginConfigurationInfo(Debug::isTty() ? Debug::Flags{} : Debug::Flag::DisableColors, importer);
Debug{} << "======================== visual color verification end =========================";
}
std::ostringstream out;
Debug redirectOutput{&out};
Implementation::printPluginConfigurationInfo(Debug::Flag::DisableColors, importer);
CORRADE_COMPARE(out.str(),
"Configuration:\n"
" # A comment\n"
" ; Another\n"
" value=yes\n"
" another=42\n"
" # Empty lines should not have trailing whitespace\n"
"\n"
" [group]\n"
" spaces=\" YES \"\n"
" newlines=\"\"\"\n"
" A\n"
" L\n"
" S\n"
" O\n"
" \"\"\"\n"
"\n"
" [group/subgroup]\n"
" subvalue=35\n"
"\n"
" # Another instance of the same group\n"
" [group]\n"
" true=false\n");
}
void ImageConverterImplementationTest::pluginConfigurationInfoDoxygenDelimiter() {
struct: Trade::AbstractImporter {
Trade::ImporterFeatures doFeatures() const override { return {}; }
bool doIsOpened() const override { return false; }
void doClose() override {}
} importer;
std::stringstream in;
in << R"(# [configuration_]
[configuration]
# A comment
value=yes
# [configuration_]
privateValue=SECRET
)";
Utility::Configuration conf{in};
importer.configuration() = Utility::ConfigurationGroup{*conf.group("configuration")};
std::ostringstream out;
Debug redirectOutput{&out};
Implementation::printPluginConfigurationInfo(Debug::Flag::DisableColors, importer);
CORRADE_COMPARE(out.str(),
"Configuration:\n"
" # A comment\n"
" value=yes\n");
}
void ImageConverterImplementationTest::importerInfo() {
/* Check if the required plugin can be loaded. Catches also ABI and
interface mismatch errors. */
if(!(_importerManager.load("AnyImageImporter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageImporter plugin can't be loaded.");
Containers::Pointer<Trade::AbstractImporter> importer = _importerManager.instantiate("AnyImageImporter");
/** @todo pick a plugin that has some actual configuration */
importer->configuration().setValue("something", "is there");
/* Print to visually verify coloring */
{
Debug{} << "======================== visual color verification start =======================";
Implementation::printImporterInfo(Debug::isTty() ? Debug::Flags{} : Debug::Flag::DisableColors, *importer);
Debug{} << "======================== visual color verification end =========================";
}
std::ostringstream out;
Debug redirectOutput{&out};
Implementation::printImporterInfo(Debug::Flag::DisableColors, *importer);
CORRADE_COMPARE(out.str(),
"Plugin name: AnyImageImporter\n"
"Features:\n"
" OpenData\n"
" FileCallback\n"
"Configuration:\n"
" something=is there\n");
}
void ImageConverterImplementationTest::converterInfo() {
/* Check if the required plugin can be loaded. Catches also ABI and
interface mismatch errors. */
if(!(_converterManager.load("AnyImageConverter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("AnyImageConverter plugin can't be loaded.");
Containers::Pointer<Trade::AbstractImageConverter> converter = _converterManager.instantiate("AnyImageConverter");
/** @todo pick a plugin that has some actual configuration */
converter->configuration().setValue("something", "is there");
/* Print to visually verify coloring */
{
Debug{} << "======================== visual color verification start =======================";
Implementation::printImageConverterInfo(Debug::isTty() ? Debug::Flags{} : Debug::Flag::DisableColors, *converter);
Debug{} << "======================== visual color verification end =========================";
}
std::ostringstream out;
Debug redirectOutput{&out};
Implementation::printImageConverterInfo(Debug::Flag::DisableColors, *converter);
CORRADE_COMPARE(out.str(),
"Plugin name: AnyImageConverter\n"
"Features:\n"
" Convert1DToFile\n"
" Convert2DToFile\n"
" Convert3DToFile\n"
" ConvertCompressed1DToFile\n"
" ConvertCompressed2DToFile\n"
" ConvertCompressed3DToFile\n"
" Levels\n"
"Configuration:\n"
" something=is there\n");
}
void ImageConverterImplementationTest::converterInfoExtensionMimeType() {
/* Check if the required plugin can be loaded. Catches also ABI and
interface mismatch errors. */
if(!(_converterManager.load("TgaImageConverter") & PluginManager::LoadState::Loaded))
CORRADE_SKIP("TgaImageConverter plugin can't be loaded.");
Containers::Pointer<Trade::AbstractImageConverter> converter = _converterManager.instantiate("TgaImageConverter");
/** @todo pick a plugin that has some actual configuration */
converter->configuration().setValue("something", "is there");
/* Print to visually verify coloring */
{
Debug{} << "======================== visual color verification start =======================";
Implementation::printImageConverterInfo(Debug::isTty() ? Debug::Flags{} : Debug::Flag::DisableColors, *converter);
Debug{} << "======================== visual color verification end =========================";
}
std::ostringstream out;
Debug redirectOutput{&out};
Implementation::printImageConverterInfo(Debug::Flag::DisableColors, *converter);
CORRADE_COMPARE(out.str(),
"Plugin name: TgaImageConverter\n"
"Features:\n"
" Convert2DToData\n"
"File extension: tga\n"
"MIME type: image/x-tga\n"
"Configuration:\n"
" something=is there\n");
} }
void ImageConverterImplementationTest::info() { void ImageConverterImplementationTest::info() {

27
src/Magnum/Trade/Test/ImageConverterTest.cpp

@ -34,6 +34,7 @@
#include <Corrade/Utility/Path.h> #include <Corrade/Utility/Path.h>
#include "Magnum/Trade/AbstractImporter.h" #include "Magnum/Trade/AbstractImporter.h"
#include "Magnum/Trade/AbstractImageConverter.h"
#include "configure.h" #include "configure.h"
@ -51,21 +52,38 @@ const struct {
const char* name; const char* name;
Containers::Array<Containers::String> args; Containers::Array<Containers::String> args;
const char* requiresImporter; const char* requiresImporter;
const char* requiresConverter;
const char* expected; const char* expected;
} InfoData[]{ } InfoData[]{
{"importer", Containers::array<Containers::String>({
"--info-importer", "-i", "someOption=yes"}),
"AnyImageImporter", nullptr,
"info-importer.txt"},
{"converter", Containers::array<Containers::String>({
"-C", "AnyImageConverter", "--info-converter", "-c", "someOption=yes"}),
nullptr, "AnyImageConverter",
"info-converter.txt"},
{"converter, implicit", Containers::array<Containers::String>({
"--info-converter", "-c", "someOption=yes"}),
nullptr, "AnyImageConverter",
"info-converter.txt"},
{"importer, ignored input and output", Containers::array<Containers::String>({
"--info-importer", "a.png", "b.png", "out.jpg"}),
"AnySceneImporter", nullptr,
"info-importer-ignored-input-output.txt"},
{"data", Containers::array<Containers::String>({ {"data", Containers::array<Containers::String>({
"-I", "TgaImporter", "--info", Utility::Path::join(TRADE_TEST_DIR, "ImageConverterTestFiles/file.tga")}), "-I", "TgaImporter", "--info", Utility::Path::join(TRADE_TEST_DIR, "ImageConverterTestFiles/file.tga")}),
"TgaImporter", "TgaImporter", nullptr,
"info-data.txt"}, "info-data.txt"},
{"data, map", Containers::array<Containers::String>({ {"data, map", Containers::array<Containers::String>({
"--map", "-I", "TgaImporter", "--info", Utility::Path::join(TRADE_TEST_DIR, "ImageConverterTestFiles/file.tga")}), "--map", "-I", "TgaImporter", "--info", Utility::Path::join(TRADE_TEST_DIR, "ImageConverterTestFiles/file.tga")}),
"TgaImporter", "TgaImporter", nullptr,
/** @todo change to something else once we have a plugin that can /** @todo change to something else once we have a plugin that can
zero-copy pass the imported data */ zero-copy pass the imported data */
"info-data.txt"}, "info-data.txt"},
{"data, ignored output file", Containers::array<Containers::String>({ {"data, ignored output file", Containers::array<Containers::String>({
"-I", "TgaImporter", "--info", Utility::Path::join(TRADE_TEST_DIR, "ImageConverterTestFiles/file.tga"), "whatever.png"}), "-I", "TgaImporter", "--info", Utility::Path::join(TRADE_TEST_DIR, "ImageConverterTestFiles/file.tga"), "whatever.png"}),
"TgaImporter", "TgaImporter", nullptr,
"info-data-ignored-output.txt"} "info-data-ignored-output.txt"}
}; };
@ -122,8 +140,11 @@ void ImageConverterTest::info() {
/* Check if required plugins can be loaded. Catches also ABI and interface /* Check if required plugins can be loaded. Catches also ABI and interface
mismatch errors. */ mismatch errors. */
PluginManager::Manager<Trade::AbstractImporter> importerManager{MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR}; PluginManager::Manager<Trade::AbstractImporter> importerManager{MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR};
PluginManager::Manager<Trade::AbstractImageConverter> converterManager{MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR};
if(data.requiresImporter && !(importerManager.load(data.requiresImporter) & PluginManager::LoadState::Loaded)) if(data.requiresImporter && !(importerManager.load(data.requiresImporter) & PluginManager::LoadState::Loaded))
CORRADE_SKIP(data.requiresImporter << "plugin can't be loaded."); CORRADE_SKIP(data.requiresImporter << "plugin can't be loaded.");
if(data.requiresConverter && !(converterManager.load(data.requiresConverter) & PluginManager::LoadState::Loaded))
CORRADE_SKIP(data.requiresConverter << "plugin can't be loaded.");
CORRADE_VERIFY(true); /* capture correct function name */ CORRADE_VERIFY(true); /* capture correct function name */

11
src/Magnum/Trade/Test/ImageConverterTestFiles/info-converter.txt

@ -0,0 +1,11 @@
Plugin name: AnyImageConverter
Features:
Convert1DToFile
Convert2DToFile
Convert3DToFile
ConvertCompressed1DToFile
ConvertCompressed2DToFile
ConvertCompressed3DToFile
Levels
Configuration:
someOption=yes

6
src/Magnum/Trade/Test/ImageConverterTestFiles/info-importer-ignored-input-output.txt

@ -0,0 +1,6 @@
Ignoring input files for --info: a.png b.png
Ignoring output file for --info: out.jpg
Plugin name: AnyImageImporter
Features:
OpenData
FileCallback

6
src/Magnum/Trade/Test/ImageConverterTestFiles/info-importer.txt

@ -0,0 +1,6 @@
Plugin name: AnyImageImporter
Features:
OpenData
FileCallback
Configuration:
someOption=yes

7
src/Magnum/Trade/Test/configure.h.cmake

@ -26,21 +26,28 @@
#define TRADE_TEST_DIR "${TRADE_TEST_DIR}" #define TRADE_TEST_DIR "${TRADE_TEST_DIR}"
#define TRADE_TEST_OUTPUT_DIR "${TRADE_TEST_OUTPUT_DIR}" #define TRADE_TEST_OUTPUT_DIR "${TRADE_TEST_OUTPUT_DIR}"
#cmakedefine IMAGECONVERTER_EXECUTABLE_FILENAME "${IMAGECONVERTER_EXECUTABLE_FILENAME}" #cmakedefine IMAGECONVERTER_EXECUTABLE_FILENAME "${IMAGECONVERTER_EXECUTABLE_FILENAME}"
#cmakedefine ANYIMAGEIMPORTER_PLUGIN_FILENAME "${ANYIMAGEIMPORTER_PLUGIN_FILENAME}"
#cmakedefine ANYIMAGECONVERTER_PLUGIN_FILENAME "${ANYIMAGECONVERTER_PLUGIN_FILENAME}"
#cmakedefine TGAIMAGECONVERTER_PLUGIN_FILENAME "${TGAIMAGECONVERTER_PLUGIN_FILENAME}"
#ifdef CORRADE_TARGET_WINDOWS #ifdef CORRADE_TARGET_WINDOWS
#ifdef CORRADE_IS_DEBUG_BUILD #ifdef CORRADE_IS_DEBUG_BUILD
#define MAGNUM_PLUGINS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}" #define MAGNUM_PLUGINS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_DEBUG_BINARY_INSTALL_DIR}"
#define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_DEBUG_BINARY_INSTALL_DIR}" #define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_DEBUG_BINARY_INSTALL_DIR}"
#define MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_BINARY_INSTALL_DIR}"
#else #else
#define MAGNUM_PLUGINS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}" #define MAGNUM_PLUGINS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_RELEASE_BINARY_INSTALL_DIR}"
#define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_RELEASE_BINARY_INSTALL_DIR}" #define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_RELEASE_BINARY_INSTALL_DIR}"
#define MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_BINARY_INSTALL_DIR}"
#endif #endif
#else #else
#ifdef CORRADE_IS_DEBUG_BUILD #ifdef CORRADE_IS_DEBUG_BUILD
#define MAGNUM_PLUGINS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR}" #define MAGNUM_PLUGINS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_DEBUG_LIBRARY_INSTALL_DIR}"
#define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_DEBUG_LIBRARY_INSTALL_DIR}" #define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_DEBUG_LIBRARY_INSTALL_DIR}"
#define MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMAGECONVERTER_DEBUG_LIBRARY_INSTALL_DIR}"
#else #else
#define MAGNUM_PLUGINS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}" #define MAGNUM_PLUGINS_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_RELEASE_LIBRARY_INSTALL_DIR}"
#define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_RELEASE_LIBRARY_INSTALL_DIR}" #define MAGNUM_PLUGINS_IMPORTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMPORTER_RELEASE_LIBRARY_INSTALL_DIR}"
#define MAGNUM_PLUGINS_IMAGECONVERTER_INSTALL_DIR "${CMAKE_INSTALL_PREFIX}/${MAGNUM_PLUGINS_IMAGECONVERTER_RELEASE_LIBRARY_INSTALL_DIR}"
#endif #endif
#endif #endif

95
src/Magnum/Trade/imageconverter.cpp

@ -192,7 +192,8 @@ magnum-imageconverter [-h|--help] [-I|--importer PLUGIN]
[-i|--importer-options key=val,key2=val2,] [-i|--importer-options key=val,key2=val2,]
[-c|--converter-options key=val,key2=val2,]... [-D|--dimensions N] [-c|--converter-options key=val,key2=val2,]... [-D|--dimensions N]
[--image N] [--level N] [--layer N] [--layers] [--levels] [--in-place] [--image N] [--level N] [--layer N] [--layers] [--levels] [--in-place]
[--info] [--color on|off|auto] [-v|--verbose] [--profile] [--] input output [--info-importer] [--info-converter] [--info] [--color on|off|auto]
[-v|--verbose] [--profile] [--] input output
@endcode @endcode
Arguments: Arguments:
@ -221,6 +222,8 @@ Arguments:
more more
- `--levels` --- combine multiple image levels into a single file - `--levels` --- combine multiple image levels into a single file
- `--in-place` --- overwrite the input image with the output - `--in-place` --- overwrite the input image with the output
- `--info-importer` --- print info about the importer plugin and exit
- `--info-converter` --- print info about the image converter plugin and exit
- `--info` --- print info about the input file and exit - `--info` --- print info about the input file and exit
- `--color` --- colored output for `--info` (default: `auto`) - `--color` --- colored output for `--info` (default: `auto`)
- `-v`, `--verbose` --- verbose output from importer and converter plugins - `-v`, `--verbose` --- verbose output from importer and converter plugins
@ -231,9 +234,16 @@ tightly-packed square of pixels in given @ref PixelFormat. Specifying `-C` /
`--converter raw` will save raw imported data instead of using a converter `--converter raw` will save raw imported data instead of using a converter
plugin. plugin.
If `--info` is given, the utility will print information about all images If the `--info-importer` or `--info-converter` option is given, the utility
present in the file, independently of the `-D` / `--dimensions` option. In this will print information about given plugin specified via the `-I` or `-C`
case no conversion is done and output file doesn't need to be specified. option, including its configuration options potentially overriden with
`-i` or `-c`. In this case no file is read and no conversion is done and
neither the input nor the output file needs to be specified.
If `--info` is given, the utility will print information about given data,
independently of the `-D` / `--dimensions` option. In this case the input file
is read but no no conversion is done and output file doesn't need to be
specified.
The `-i` / `--importer-options` and `-c` / `--converter-options` arguments The `-i` / `--importer-options` and `-c` / `--converter-options` arguments
accept a comma-separated list of key/value pairs to set in the importer / accept a comma-separated list of key/value pairs to set in the importer /
@ -258,6 +268,11 @@ using namespace Containers::Literals;
namespace { namespace {
bool isPluginInfoRequested(const Utility::Arguments& args) {
return args.isSet("info-importer") ||
args.isSet("info-converter");
}
template<UnsignedInt dimensions> bool checkCommonFormatFlags(const Utility::Arguments& args, const Containers::Array<Trade::ImageData<dimensions>>& images) { template<UnsignedInt dimensions> bool checkCommonFormatFlags(const Utility::Arguments& args, const Containers::Array<Trade::ImageData<dimensions>>& images) {
CORRADE_INTERNAL_ASSERT(!images.isEmpty()); CORRADE_INTERNAL_ASSERT(!images.isEmpty());
const bool compressed = images.front().isCompressed(); const bool compressed = images.front().isCompressed();
@ -362,15 +377,21 @@ int main(int argc, char** argv) {
.addBooleanOption("layers").setHelp("layers", "combine multiple layers into an image with one dimension more") .addBooleanOption("layers").setHelp("layers", "combine multiple layers into an image with one dimension more")
.addBooleanOption("levels").setHelp("layers", "combine multiple image levels into a single file") .addBooleanOption("levels").setHelp("layers", "combine multiple image levels into a single file")
.addBooleanOption("in-place").setHelp("in-place", "overwrite the input image with the output") .addBooleanOption("in-place").setHelp("in-place", "overwrite the input image with the output")
.addBooleanOption("info-importer").setHelp("info-importer", "print info about the importer plugin and exit")
.addBooleanOption("info-converter").setHelp("info-converter", "print info about the image converter plugin and exit")
.addBooleanOption("info").setHelp("info", "print info about the input file and exit") .addBooleanOption("info").setHelp("info", "print info about the input file and exit")
.addOption("color", "auto").setHelp("color", "colored output for --info", "on|off|auto") .addOption("color", "auto").setHelp("color", "colored output for --info", "on|off|auto")
.addBooleanOption('v', "verbose").setHelp("verbose", "verbose output from importer and converter plugins") .addBooleanOption('v', "verbose").setHelp("verbose", "verbose output from importer and converter plugins")
.addBooleanOption("profile").setHelp("profile", "measure import and conversion time") .addBooleanOption("profile").setHelp("profile", "measure import and conversion time")
.setParseErrorCallback([](const Utility::Arguments& args, Utility::Arguments::ParseError error, const std::string& key) { .setParseErrorCallback([](const Utility::Arguments& args, Utility::Arguments::ParseError error, const std::string& key) {
/* If --in-place or --info is passed, we don't need the output /* If --info for plugins is passed, we don't need the input */
argument */ if(error == Utility::Arguments::ParseError::MissingArgument &&
key == "input" && isPluginInfoRequested(args))
return true;
/* If --in-place or --info for plugins or data is passed, we don't
need the output argument */
if(error == Utility::Arguments::ParseError::MissingArgument && if(error == Utility::Arguments::ParseError::MissingArgument &&
key == "output" && (args.isSet("in-place") || args.isSet("info"))) key == "output" && (args.isSet("in-place") || isPluginInfoRequested(args) || args.isSet("info")))
return true; return true;
/* Handle all other errors as usual */ /* Handle all other errors as usual */
@ -382,9 +403,14 @@ Specifying --importer raw:<format> will treat the input as a raw tightly-packed
square of pixels in given pixel format. Specifying -C / --converter raw will square of pixels in given pixel format. Specifying -C / --converter raw will
save raw imported data instead of using a converter plugin. save raw imported data instead of using a converter plugin.
If --info is given, the utility will print information about all images present If the --info-importer or --info-converter option is given, the utility will
in the file, independently of the -D / --dimensions option. In this case no print information about given plugin specified via the -I or -C option,
conversion is done and output file doesn't need to be specified. including its configuration options potentially overriden with -i or -c. In
this case no file is read and no conversion is done and neither the input nor
the output file needs to be specified.
If --info is given, the utility will print information about given data, independently of the -D / --dimensions option. In this case the input file is
read but no conversion is done and output file doesn't need to be specified.
The -i / --importer-options and -c / --converter-options arguments accept a The -i / --importer-options and -c / --converter-options arguments accept a
comma-separated list of key/value pairs to set in the importer / converter comma-separated list of key/value pairs to set in the importer / converter
@ -410,16 +436,26 @@ no -C / --converter is specified, AnyImageConverter is used.)")
useColor = Debug::isTty() ? Debug::Flags{} : Debug::Flag::DisableColors; useColor = Debug::isTty() ? Debug::Flags{} : Debug::Flag::DisableColors;
/* Generic checks */ /* Generic checks */
if(const std::size_t inputCount = args.arrayValueCount("input")) {
/* Not an error in this case, it should be possible to just append
--info* to existing command line without having to remove anything.
But print a warning at least, it could also be a mistyped option. */
if(isPluginInfoRequested(args)) {
Warning w;
w << "Ignoring input files for --info:";
for(std::size_t i = 0; i != inputCount; ++i)
w << args.arrayValue<Containers::StringView>("input", i);
}
}
if(args.value<Containers::StringView>("output")) { if(args.value<Containers::StringView>("output")) {
if(args.isSet("in-place")) { if(args.isSet("in-place")) {
Error{} << "Output file shouldn't be set for --in-place:" << args.value<Containers::StringView>("output"); Error{} << "Output file shouldn't be set for --in-place:" << args.value<Containers::StringView>("output");
return 1; return 1;
} }
/* Not an error in this case, it should be possible to just append /* Same as above, it should be possible to just append --info* to
--info to existing command line without having to remove anything. existing command line */
But print a warning at least, it could also be a mistyped option. */ if(isPluginInfoRequested(args) || args.isSet("info"))
if(args.isSet("info"))
Warning{} << "Ignoring output file for --info:" << args.value<Containers::StringView>("output"); Warning{} << "Ignoring output file for --info:" << args.value<Containers::StringView>("output");
} }
@ -450,7 +486,7 @@ no -C / --converter is specified, AnyImageConverter is used.)")
Error{} << "The --levels option can't be combined with raw data output"; Error{} << "The --levels option can't be combined with raw data output";
return 1; return 1;
} }
if(!args.isSet("layers") && !args.isSet("levels") && args.arrayValueCount("input") > 1) { if(!args.isSet("layers") && !args.isSet("levels") && args.arrayValueCount("input") > 1 && !isPluginInfoRequested(args)) {
Error{} << "Multiple input files require the --layers / --levels option to be set"; Error{} << "Multiple input files require the --layers / --levels option to be set";
return 1; return 1;
} }
@ -463,6 +499,35 @@ no -C / --converter is specified, AnyImageConverter is used.)")
args.value("plugin-dir").empty() ? Containers::String{} : args.value("plugin-dir").empty() ? Containers::String{} :
Utility::Path::join(args.value("plugin-dir"), Utility::Path::split(Trade::AbstractImageConverter::pluginSearchPaths().back()).second())}; Utility::Path::join(args.value("plugin-dir"), Utility::Path::split(Trade::AbstractImageConverter::pluginSearchPaths().back()).second())};
/* Print plugin info, if requested */
if(args.isSet("info-importer")) {
Containers::Pointer<Trade::AbstractImporter> importer = importerManager.loadAndInstantiate(args.value("importer"));
if(!importer) {
Debug{} << "Available importer plugins:" << ", "_s.join(importerManager.aliasList());
return 1;
}
/* Set options, if passed */
if(args.isSet("verbose")) importer->addFlags(Trade::ImporterFlag::Verbose);
Implementation::setOptions(*importer, "AnyImageImporter", args.value("importer-options"));
Trade::Implementation::printImporterInfo(useColor, *importer);
return 0;
}
if(args.isSet("info-converter")) {
Containers::Pointer<Trade::AbstractImageConverter> converter = converterManager.loadAndInstantiate(args.arrayValueCount("converter") ? args.arrayValue("converter", 0) : "AnyImageConverter");
if(!converter) {
Debug{} << "Available converter plugins:" << ", "_s.join(converterManager.aliasList());
return 1;
}
/* Set options, if passed */
if(args.isSet("verbose")) converter->addFlags(Trade::ImageConverterFlag::Verbose);
if(args.arrayValueCount("converter-options"))
Implementation::setOptions(*converter, "AnyImageConverter", args.arrayValue("converter-options", 0));
Trade::Implementation::printImageConverterInfo(useColor, *converter);
return 0;
}
const Int dimensions = args.value<Int>("dimensions"); const Int dimensions = args.value<Int>("dimensions");
/** @todo make them array options as well? */ /** @todo make them array options as well? */
const UnsignedInt image = args.value<UnsignedInt>("image"); const UnsignedInt image = args.value<UnsignedInt>("image");

Loading…
Cancel
Save