Browse Source

{scene,image}converter: colored and more compact output for --info.

Because the uncolored overly verbose output was ridicilously ugly and
hard to navigate for larger scenes. Like with TestSuite executables,
there's a --color option that defaults to automatic coloring based on
whether printing to a TTY and can be both explicitly enabled and
disabled.
pull/556/head
Vladimír Vondruš 4 years ago
parent
commit
823716a475
  1. 4
      doc/changelog.dox
  2. 258
      src/Magnum/SceneTools/sceneconverter.cpp
  3. 9
      src/Magnum/Trade/Implementation/converterUtilities.h
  4. 43
      src/Magnum/Trade/imageconverter.cpp

4
doc/changelog.dox

@ -212,6 +212,8 @@ See also:
experimental `--concatenate-meshes` option, which will flatten the mesh experimental `--concatenate-meshes` option, which will flatten the mesh
hierarchy and concatenate all meshes together. Note that it doesn't hierarchy and concatenate all meshes together. Note that it doesn't
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
now more compact and colored for better readability
@subsubsection changelog-latest-new-shaders Shaders library @subsubsection changelog-latest-new-shaders Shaders library
@ -288,6 +290,8 @@ See also:
@ref magnum-imageconverter "magnum-imageconverter" utility, as well as @ref magnum-imageconverter "magnum-imageconverter" utility, as well as
combining layers into images of one dimension more (or vice versa) and combining layers into images of one dimension more (or vice versa) and
creating multi-level images from separate input files creating multi-level images from separate input files
- The @ref magnum-imageconverter "magnum-imageconverter" `--info` output is
now more compact and colored for better readability
@subsubsection changelog-latest-new-vk Vk library @subsubsection changelog-latest-new-vk Vk library

258
src/Magnum/SceneTools/sceneconverter.cpp

@ -23,6 +23,7 @@
DEALINGS IN THE SOFTWARE. DEALINGS IN THE SOFTWARE.
*/ */
#include <cctype> /* std::isupper() */
#include <sstream> #include <sstream>
#include <Corrade/Containers/Optional.h> #include <Corrade/Containers/Optional.h>
#include <Corrade/Containers/Pair.h> #include <Corrade/Containers/Pair.h>
@ -87,8 +88,8 @@ magnum-sceneconverter [-h|--help] [-I|--importer IMPORTER]
[-c|--converter-options key=val,key2=val2,]... [--mesh MESH] [-c|--converter-options key=val,key2=val2,]... [--mesh MESH]
[--level LEVEL] [--concatenate-meshes] [--info-animations] [--info-images] [--level LEVEL] [--concatenate-meshes] [--info-animations] [--info-images]
[--info-lights] [--info-materials] [--info-meshes] [--info-skins] [--info-lights] [--info-materials] [--info-meshes] [--info-skins]
[--info-textures] [--info] [--bounds] [-v|--verbose] [--profile] [--info-textures] [--info] [--color on|off|auto] [--bounds] [-v|--verbose]
[--] input output [--profile] [--] input output
@endcode @endcode
Arguments: Arguments:
@ -131,6 +132,7 @@ Arguments:
- `--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 `--info-*` options together
- `--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
@ -260,6 +262,7 @@ int main(int argc, char** argv) {
.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 --info-* options together")
.addOption("color", "auto").setHelp("color", "colored output for --info", "on|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")
@ -719,31 +722,40 @@ is specified as well, the IDs reference attributes of the first mesh.)")
} }
} }
/* In case the images have all just a single level and no names, write
them in a compact way without listing levels. */
bool compactImages = true;
Containers::Array<Trade::Implementation::ImageInfo> imageInfos; Containers::Array<Trade::Implementation::ImageInfo> imageInfos;
if(args.isSet("info") || args.isSet("info-images")) { if(args.isSet("info") || args.isSet("info-images")) {
imageInfos = Trade::Implementation::imageInfo(*importer, error, compactImages, importTime); imageInfos = Trade::Implementation::imageInfo(*importer, error, importTime);
} }
/* Colored output. Enable only if a TTY. */
Debug::Flags useColor;
if(args.value("color") == "on")
useColor = Debug::Flags{};
else if(args.value("color") == "off")
useColor = Debug::Flag::DisableColors;
else
useColor = Debug::isTty() ? Debug::Flags{} : Debug::Flag::DisableColors;
for(const SceneInfo& info: sceneInfos) { for(const SceneInfo& info: sceneInfos) {
Debug d; Debug d{useColor};
d << "Scene" << info.scene << Debug::nospace << ":"; d << Debug::boldColor(Debug::Color::White) << "Scene" << info.scene << Debug::nospace << ":" << Debug::resetColor;
if(!info.name.empty()) d << info.name; if(!info.name.empty()) d << Debug::boldColor(Debug::Color::Yellow) << info.name << Debug::resetColor;
d << Debug::newline; d << Debug::newline;
d << " bound:" << info.mappingBound << "objects," << info.mappingType d << " Bound:" << info.mappingBound << "objects" << Debug::color(Debug::Color::Blue) << "@" << Debug::packed << Debug::color(Debug::Color::Cyan) << info.mappingType << Debug::resetColor
<< "(" << Debug::nospace << Utility::format("{:.1f}", info.dataSize/1024.0f) << "kB)"; << "(" << Debug::nospace << Utility::format("{:.1f}", info.dataSize/1024.0f) << "kB)";
d << Debug::newline << " Fields:";
for(const SceneFieldInfo& field: info.fields) { for(const SceneFieldInfo& field: info.fields) {
d << Debug::newline << " " << field.name; d << Debug::newline << " " << Debug::packed << Debug::boldColor(Debug::Color::White) << field.name << Debug::resetColor;
if(Trade::isSceneFieldCustom(field.name)) { if(Trade::isSceneFieldCustom(field.name)) {
d << "(" << Debug::nospace << field.customName d << Debug::color(Debug::Color::Yellow) << field.customName
<< Debug::nospace << ")"; << Debug::resetColor;
} }
d << "@" << field.type; d << Debug::color(Debug::Color::Blue) << "@" << Debug::packed << Debug::color(Debug::Color::Cyan) << field.type;
if(field.arraySize) if(field.arraySize)
d << Debug::nospace << Utility::format("[{}]", field.arraySize); d << Debug::nospace << Utility::format("[{}]", field.arraySize);
d << Debug::resetColor;
d << Debug::nospace << "," << field.size << "entries"; d << Debug::nospace << "," << field.size << "entries";
if(field.flags) if(field.flags)
d << Debug::newline << " " << field.flags; d << Debug::newline << " " << field.flags;
@ -751,92 +763,125 @@ is specified as well, the IDs reference attributes of the first mesh.)")
} }
for(const AnimationInfo& info: animationInfos) { for(const AnimationInfo& info: animationInfos) {
Debug d; Debug d{useColor};
d << "Animation" << info.animation << Debug::nospace << ":"; d << Debug::boldColor(Debug::Color::White) << "Animation" << info.animation << Debug::nospace << ":" << Debug::resetColor;
if(!info.name.empty()) d << info.name; if(!info.name.empty()) d << Debug::boldColor(Debug::Color::Yellow) << info.name << Debug::resetColor;
d << Debug::newline << " Duration:" << info.data.duration(); d << Debug::newline << " Duration:" << info.data.duration()
<< "(" << Debug::nospace << Utility::format("{:.1f}", info.data.data().size()/1024.0f) << "kB)";
for(UnsignedInt i = 0; i != info.data.trackCount(); ++i) { for(UnsignedInt i = 0; i != info.data.trackCount(); ++i) {
d << Debug::newline << " Track" << i << Debug::nospace << ":" d << Debug::newline << " Track" << i << Debug::nospace << ":"
<< info.data.trackType(i); << Debug::packed << Debug::boldColor(Debug::Color::White)
<< info.data.trackTargetType(i)
<< Debug::color(Debug::Color::Blue) << "@"
<< Debug::packed << Debug::color(Debug::Color::Cyan)
<< info.data.trackType(i) << Debug::resetColor;
if(info.data.trackType(i) != info.data.trackResultType(i)) if(info.data.trackType(i) != info.data.trackResultType(i))
d << "->" << info.data.trackResultType(i); d << Debug::color(Debug::Color::Blue) << "->"
<< Debug::packed << Debug::color(Debug::Color::Cyan)
<< info.data.trackResultType(i) << Debug::resetColor;
d << Debug::nospace << "," << info.data.track(i).size() d << Debug::nospace << "," << info.data.track(i).size()
<< "keyframes"; << "keyframes";
if(info.data.track(i).duration() != info.data.duration()) if(info.data.track(i).duration() != info.data.duration())
d << Debug::nospace << "," << info.data.track(i).duration(); d << Debug::nospace << "," << info.data.track(i).duration();
d << Debug::newline << " " d << Debug::newline
<< info.data.track(i).interpolation(); << " Interpolation:"
d << Debug::newline << " " << Debug::packed << Debug::color(Debug::Color::Cyan)
<< info.data.track(i).before() << Debug::nospace << "," << info.data.track(i).interpolation() << Debug::resetColor
<< info.data.track(i).after(); << Debug::nospace << "," << Debug::packed
d << Debug::newline << " Target: object" << Debug::color(Debug::Color::Cyan)
<< info.data.trackTarget(i) << Debug::nospace << "," << info.data.track(i).before() << Debug::resetColor
<< info.data.trackTargetType(i); << Debug::nospace << "," << Debug::packed
<< Debug::color(Debug::Color::Cyan)
<< info.data.track(i).after() << Debug::resetColor;
/** @todo might be useful to show bounds here as well, though /** @todo might be useful to show bounds here as well, though
not so much for things like complex numbers or quats */ not so much for things like complex numbers or quats */
} }
} }
for(const SkinInfo& info: skinInfos) { for(const SkinInfo& info: skinInfos) {
Debug d; Debug d{useColor};
d << "Skin" << info.skin; d << Debug::boldColor(Debug::Color::White) << "Skin" << info.skin
<< Debug::resetColor;
/* Print reference count only if there actually are scenes and they /* Print reference count only if there actually are scenes and they
were parsed, otherwise this information is useless */ were parsed, otherwise this information is useless */
if(skinReferenceCount) if(skinReferenceCount)
d << Utility::format("(referenced by {} objects)", skinReferenceCount[info.skin]); d << Utility::format("(referenced by {} objects)", skinReferenceCount[info.skin]);
d << Debug::nospace << ":"; d << Debug::boldColor(Debug::Color::White) << Debug::nospace << ":"
if(!info.name.empty()) d << info.name; << Debug::resetColor;
if(!info.name.empty()) d << Debug::boldColor(Debug::Color::Yellow)
<< info.name << Debug::resetColor;
d << Debug::newline << " Joints:" << info.data.joints(); d << Debug::newline << " " << info.data.joints().size() << "joints";
} }
for(const LightInfo& info: lightInfos) { for(const LightInfo& info: lightInfos) {
Debug d; Debug d{useColor};
d << "Light" << info.light; d << Debug::boldColor(Debug::Color::White) << "Light" << info.light << Debug::resetColor;
/* Print reference count only if there actually are scenes and they /* Print reference count only if there actually are scenes and they
were parsed, otherwise this information is useless */ were parsed, otherwise this information is useless */
if(lightReferenceCount) if(lightReferenceCount)
d << Utility::format("(referenced by {} objects)", lightReferenceCount[info.light]); d << Utility::format("(referenced by {} objects)", lightReferenceCount[info.light]);
d << Debug::nospace << ":"; d << Debug::boldColor(Debug::Color::White) << Debug::nospace << ":"
if(!info.name.empty()) d << info.name; << Debug::resetColor;
if(!info.name.empty()) d << Debug::boldColor(Debug::Color::Yellow)
<< info.name << Debug::resetColor;
d << Debug::newline << " Type:" << info.data.type(); d << Debug::newline << " Type:" << Debug::packed
d << Debug::newline << " Color:" << info.data.color(); << Debug::color(Debug::Color::Cyan)
d << Debug::newline << " Intensity:" << info.data.intensity(); << info.data.type() << Debug::resetColor;
d << Debug::newline << " Attenuation:" << info.data.attenuation();
d << Debug::newline << " Range:" << info.data.range();
if(info.data.type() == Trade::LightData::Type::Spot) if(info.data.type() == Trade::LightData::Type::Spot)
d << Debug::newline << " Cone angles:" << Deg(info.data.innerConeAngle()) << Deg(info.data.outerConeAngle()); d << Debug::nospace << "," << Debug::packed
<< Deg(info.data.innerConeAngle()) << Debug::nospace
<< "° -" << Debug::packed << Deg(info.data.outerConeAngle())
<< Debug::nospace << "°";
d << Debug::newline << " Color:" << Debug::packed
<< info.data.color();
if(!Math::equal(info.data.intensity(), 1.0f))
d << "*" << info.data.intensity();
d << Debug::newline << " Attenuation:" << Debug::packed
<< info.data.attenuation();
if(info.data.range() != Constants::inf())
d << Debug::newline << " Range:" << Debug::packed
<< info.data.range();
} }
for(const MaterialInfo& info: materialInfos) { for(const MaterialInfo& info: materialInfos) {
Debug d; Debug d{useColor};
d << "Material" << info.material; d << Debug::boldColor(Debug::Color::White) << "Material" << info.material << Debug::resetColor;
/* Print reference count only if there actually are scenes and they /* Print reference count only if there actually are scenes and they
were parsed, otherwise this information is useless */ were parsed, otherwise this information is useless */
if(materialReferenceCount) if(materialReferenceCount)
d << Utility::format("(referenced by {} objects)", materialReferenceCount[info.material]); d << Utility::format("(referenced by {} objects)", materialReferenceCount[info.material]);
d << Debug::nospace << ":"; d << Debug::boldColor(Debug::Color::White) << Debug::nospace << ":"
if(!info.name.empty()) d << info.name; << Debug::resetColor;
if(!info.name.empty()) d << Debug::boldColor(Debug::Color::Yellow) << info.name << Debug::resetColor;
d << Debug::newline << " Type:" << info.data.types(); d << Debug::newline << " Type:" << Debug::packed << Debug::color(Debug::Color::Cyan) << info.data.types() << Debug::resetColor;
for(UnsignedInt i = 0; i != info.data.layerCount(); ++i) { for(UnsignedInt i = 0; i != info.data.layerCount(); ++i) {
/* Print extra layers with extra indent */ /* Print extra layers with extra indent */
const char* indent; const char* indent;
if(info.data.layerCount() != 1 && i != 0) { if(info.data.layerCount() != 1 && i != 0) {
d << Debug::newline << " Layer" << i << Debug::nospace << ":"; d << Debug::newline << " Layer" << i << Debug::nospace << ":";
if(!info.data.layerName(i).isEmpty()) if(!info.data.layerName(i).isEmpty()) {
d << info.data.layerName(i); if(std::isupper(info.data.layerName(i)[0]))
d << Debug::boldColor(Debug::Color::White);
else
d << Debug::color(Debug::Color::Yellow);
d << info.data.layerName(i) << Debug::resetColor;
}
indent = " "; indent = " ";
} else indent = " "; } else {
d << Debug::newline << " Base layer:";
indent = " ";
}
for(UnsignedInt j = 0; j != info.data.attributeCount(i); ++j) { for(UnsignedInt j = 0; j != info.data.attributeCount(i); ++j) {
/* Ignore layer name (which is always first) unless it's in /* Ignore layer name (which is always first) unless it's in
@ -845,16 +890,20 @@ is specified as well, the IDs reference attributes of the first mesh.)")
if(i && !j && info.data.attributeName(i, j) == " LayerName") if(i && !j && info.data.attributeName(i, j) == " LayerName")
continue; continue;
d << Debug::newline << indent d << Debug::newline << indent;
<< info.data.attributeName(i, j) << "@" if(std::isupper(info.data.attributeName(i, j)[0]))
<< info.data.attributeType(i, j) << Debug::nospace d << Debug::boldColor(Debug::Color::White);
else
d << Debug::color(Debug::Color::Yellow);
d << info.data.attributeName(i, j) << Debug::color(Debug::Color::Blue) << "@" << Debug::packed << Debug::color(Debug::Color::Cyan)
<< info.data.attributeType(i, j) << Debug::resetColor << Debug::nospace
<< ":"; << ":";
switch(info.data.attributeType(i, j)) { switch(info.data.attributeType(i, j)) {
case Trade::MaterialAttributeType::Bool: case Trade::MaterialAttributeType::Bool:
d << info.data.attribute<bool>(i, j); d << info.data.attribute<bool>(i, j);
break; break;
#define _c(type) case Trade::MaterialAttributeType::type: \ #define _c(type) case Trade::MaterialAttributeType::type: \
d << info.data.attribute<type>(i, j); \ d << Debug::packed << info.data.attribute<type>(i, j); \
break; break;
_c(Float) _c(Float)
_c(Deg) _c(Deg)
@ -891,7 +940,7 @@ is specified as well, the IDs reference attributes of the first mesh.)")
d << info.data.attribute<Containers::StringView>(i, j); d << info.data.attribute<Containers::StringView>(i, j);
break; break;
case Trade::MaterialAttributeType::TextureSwizzle: case Trade::MaterialAttributeType::TextureSwizzle:
d << info.data.attribute<Trade::MaterialTextureSwizzle>(i, j); d << Debug::packed << info.data.attribute<Trade::MaterialTextureSwizzle>(i, j);
break; break;
} }
} }
@ -899,76 +948,95 @@ is specified as well, the IDs reference attributes of the first mesh.)")
} }
for(const MeshInfo& info: meshInfos) { for(const MeshInfo& info: meshInfos) {
Debug d; Debug d{useColor};
if(info.level == 0) { if(info.level == 0) {
d << "Mesh" << info.mesh; d << Debug::boldColor(Debug::Color::White) << "Mesh" << info.mesh << Debug::resetColor;
/* Print reference count only if there actually are scenes and /* Print reference count only if there actually are scenes and
they were parsed, otherwise this information is useless */ they were parsed, otherwise this information is useless */
if(meshReferenceCount) if(meshReferenceCount)
d << Utility::format("(referenced by {} objects)", meshReferenceCount[info.mesh]); d << Utility::format("(referenced by {} objects)", meshReferenceCount[info.mesh]);
d << Debug::nospace << ":"; d << Debug::boldColor(Debug::Color::White) << Debug::nospace << ":"
if(!info.name.empty()) d << info.name; << Debug::resetColor;
if(!info.name.empty()) d << Debug::boldColor(Debug::Color::Yellow) << info.name << Debug::resetColor;
d << Debug::newline; d << Debug::newline;
} }
d << " Level" << info.level << Debug::nospace << ":" d << " Level" << info.level << Debug::nospace << ":"
<< info.primitive << Debug::nospace << "," << info.vertexCount << info.vertexCount << "vertices" << Debug::color(Debug::Color::Blue) << "@" << Debug::packed << Debug::color(Debug::Color::Cyan) << info.primitive << Debug::resetColor << "(" << Debug::nospace
<< "vertices (" << Debug::nospace
<< Utility::format("{:.1f}", info.vertexDataSize/1024.0f) << Utility::format("{:.1f}", info.vertexDataSize/1024.0f)
<< "kB)"; << "kB)";
if(info.indexType != MeshIndexType{}) {
d << Debug::newline << " " << info.indexCount << "indices, offset" << info.indexOffset << "@"
<< info.indexType << Debug::nospace << ", stride" << info.indexStride << "(" << Debug::nospace
<< Utility::format("{:.1f}", info.indexDataSize/1024.0f)
<< "kB)";
if(!info.indexBounds.empty())
d << Debug::newline << " bounds:" << info.indexBounds;
}
for(const MeshAttributeInfo& attribute: info.attributes) { for(const MeshAttributeInfo& attribute: info.attributes) {
d << Debug::newline << " Offset" << attribute.offset d << Debug::newline << " " << Debug::packed << Debug::boldColor(Debug::Color::White) << attribute.name;
<< Debug::nospace << ":" << attribute.name;
if(Trade::isMeshAttributeCustom(attribute.name)) { if(Trade::isMeshAttributeCustom(attribute.name)) {
d << "(" << Debug::nospace << attribute.customName d << Debug::color(Debug::Color::Yellow) << attribute.customName << Debug::resetColor;
<< Debug::nospace << ")";
} }
d << "@" << attribute.format; d << Debug::color(Debug::Color::Blue) << "@" << Debug::packed << Debug::color(Debug::Color::Cyan) << attribute.format;
if(attribute.arraySize) if(attribute.arraySize)
d << Debug::nospace << Utility::format("[{}]", attribute.arraySize); d << Debug::nospace << Utility::format("[{}]", attribute.arraySize);
d << Debug::resetColor;
d << Debug::nospace << ", offset" << attribute.offset;
d << Debug::nospace << ", stride" d << Debug::nospace << ", stride"
<< attribute.stride; << attribute.stride;
if(!attribute.bounds.empty()) if(!attribute.bounds.empty())
d << Debug::newline << " bounds:" << attribute.bounds; d << Debug::newline << " bounds:" << attribute.bounds;
} }
if(info.indexType != MeshIndexType{}) {
d << Debug::newline << " " << info.indexCount << "indices" << Debug::color(Debug::Color::Blue) << "@"
<< Debug::packed << Debug::color(Debug::Color::Cyan) << info.indexType << Debug::resetColor << Debug::nospace << ", offset" << info.indexOffset << Debug::nospace << ", stride" << info.indexStride << "(" << Debug::nospace
<< Utility::format("{:.1f}", info.indexDataSize/1024.0f)
<< "kB)";
if(!info.indexBounds.empty())
d << Debug::newline << " bounds:" << info.indexBounds;
}
} }
for(const TextureInfo& info: textureInfos) { for(const TextureInfo& info: textureInfos) {
Debug d; Debug d{useColor};
d << "Texture" << info.texture; d << Debug::boldColor(Debug::Color::White) << "Texture" << info.texture << Debug::resetColor;
/* Print reference count only if there actually are materials and /* Print reference count only if there actually are materials and
they were parsed, otherwise this information is useless */ they were parsed, otherwise this information is useless */
if(textureReferenceCount) if(textureReferenceCount)
d << Utility::format("(referenced by {} material attributes)", textureReferenceCount[info.texture]); d << Utility::format("(referenced by {} material attributes)", textureReferenceCount[info.texture]);
d << Debug::nospace << ":"; d << Debug::boldColor(Debug::Color::White) << Debug::nospace << ":"
if(!info.name.empty()) d << info.name; << Debug::resetColor;
if(!info.name.empty()) d << Debug::boldColor(Debug::Color::Yellow)
<< info.name << Debug::resetColor;
d << Debug::newline; d << Debug::newline;
d << " Type:" << info.data.type(); d << " Type:"
d << "\n Minification:" << info.data.minificationFilter() << info.data.mipmapFilter(); << Debug::packed
d << "\n Magnification:" << info.data.magnificationFilter(); << Debug::color(Debug::Color::Cyan) << info.data.type()
d << "\n Wrapping:" << info.data.wrapping(); << Debug::resetColor << Debug::nospace << ", image"
d << "\n Image:" << info.data.image(); << info.data.image();
d << Debug::newline << " Minification, mipmap and magnification:"
<< Debug::packed << Debug::color(Debug::Color::Cyan)
<< info.data.minificationFilter() << Debug::nospace << ","
<< Debug::packed << Debug::color(Debug::Color::Cyan)
<< info.data.mipmapFilter() << Debug::nospace << ","
<< Debug::packed << Debug::color(Debug::Color::Cyan)
<< info.data.magnificationFilter() << Debug::resetColor;
d << Debug::newline << " Wrapping:" << Debug::resetColor << "{" << Debug::nospace
<< Debug::packed << Debug::color(Debug::Color::Cyan)
<< info.data.wrapping()[0] << Debug::resetColor
<< Debug::nospace << "," << Debug::packed
<< Debug::color(Debug::Color::Cyan) << info.data.wrapping()[1]
<< Debug::resetColor << Debug::nospace << "," << Debug::packed
<< Debug::color(Debug::Color::Cyan) << info.data.wrapping()[1]
<< Debug::resetColor << Debug::nospace << "}";
} }
for(const Trade::Implementation::ImageInfo& info: imageInfos) { for(const Trade::Implementation::ImageInfo& info: imageInfos) {
Debug d; Debug d{useColor};
if(info.level == 0) { if(info.level == 0) {
d << Debug::boldColor(Debug::Color::White);
if(info.size.z()) d << "3D image"; if(info.size.z()) d << "3D image";
else if(info.size.y()) d << "2D image"; else if(info.size.y()) d << "2D image";
else d << "1D image"; else d << "1D image";
d << info.image; d << info.image << Debug::resetColor;
/* Print reference count only if there actually are textures /* Print reference count only if there actually are textures
and they were parsed otherwise this information is and they were parsed otherwise this information is
@ -980,16 +1048,22 @@ is specified as well, the IDs reference attributes of the first mesh.)")
else if(image1DReferenceCount) else if(image1DReferenceCount)
d << Utility::format("(referenced by {} textures)", image1DReferenceCount[info.image]); d << Utility::format("(referenced by {} textures)", image1DReferenceCount[info.image]);
d << Debug::nospace << ":"; d << Debug::boldColor(Debug::Color::White) << Debug::nospace << ":"
if(!info.name.empty()) d << info.name; << Debug::resetColor;
if(!compactImages) d << Debug::newline; if(!info.name.empty()) d << Debug::boldColor(Debug::Color::Yellow)
<< info.name << Debug::resetColor;
d << Debug::newline;
} }
if(!compactImages) d << " Level" << info.level << Debug::nospace << ":"; d << " Level" << info.level << Debug::nospace << ":"
if(info.compressed) d << info.compressedFormat; << Debug::packed;
else d << info.format;
if(info.size.z()) d << info.size; if(info.size.z()) d << info.size;
else if(info.size.y()) d << info.size.xy(); else if(info.size.y()) d << info.size.xy();
else d << Math::Vector<1, Int>(info.size.x()); else d << Math::Vector<1, Int>(info.size.x());
d << Debug::color(Debug::Color::Blue) << "@" << Debug::resetColor;
d << Debug::packed;
if(info.compressed) d << Debug::color(Debug::Color::Yellow) << info.compressedFormat;
else d << Debug::color(Debug::Color::Cyan) << info.format;
d << Debug::resetColor << "(" << Debug::nospace << Utility::format("{:.1f}", info.dataSize/1024.0f) << "kB)";
} }
if(args.isSet("profile")) { if(args.isSet("profile")) {

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

@ -54,15 +54,15 @@ struct ImageInfo {
PixelFormat format; PixelFormat format;
CompressedPixelFormat compressedFormat; CompressedPixelFormat compressedFormat;
Vector3i size; Vector3i size;
std::size_t dataSize;
std::string name; std::string name;
}; };
Containers::Array<ImageInfo> imageInfo(AbstractImporter& importer, bool& error, bool& compact, std::chrono::high_resolution_clock::duration& importTime) { Containers::Array<ImageInfo> imageInfo(AbstractImporter& importer, bool& error, std::chrono::high_resolution_clock::duration& importTime) {
Containers::Array<ImageInfo> infos; Containers::Array<ImageInfo> infos;
for(UnsignedInt i = 0; i != importer.image1DCount(); ++i) { for(UnsignedInt i = 0; i != importer.image1DCount(); ++i) {
const std::string name = importer.image1DName(i); const std::string name = importer.image1DName(i);
const UnsignedInt levelCount = importer.image1DLevelCount(i); const UnsignedInt levelCount = importer.image1DLevelCount(i);
if(!name.empty() || levelCount != 1) compact = false;
for(UnsignedInt j = 0; j != levelCount; ++j) { for(UnsignedInt j = 0; j != levelCount; ++j) {
Containers::Optional<Trade::ImageData1D> image; Containers::Optional<Trade::ImageData1D> image;
@ -80,13 +80,13 @@ Containers::Array<ImageInfo> imageInfo(AbstractImporter& importer, bool& error,
image->isCompressed() ? image->isCompressed() ?
image->compressedFormat() : CompressedPixelFormat{}, image->compressedFormat() : CompressedPixelFormat{},
Vector3i::pad(image->size()), Vector3i::pad(image->size()),
image->data().size(),
j ? "" : importer.image1DName(i)); j ? "" : importer.image1DName(i));
} }
} }
for(UnsignedInt i = 0; i != importer.image2DCount(); ++i) { for(UnsignedInt i = 0; i != importer.image2DCount(); ++i) {
const std::string name = importer.image2DName(i); const std::string name = importer.image2DName(i);
const UnsignedInt levelCount = importer.image2DLevelCount(i); const UnsignedInt levelCount = importer.image2DLevelCount(i);
if(!name.empty() || levelCount != 1) compact = false;
for(UnsignedInt j = 0; j != levelCount; ++j) { for(UnsignedInt j = 0; j != levelCount; ++j) {
Containers::Optional<Trade::ImageData2D> image; Containers::Optional<Trade::ImageData2D> image;
@ -104,13 +104,13 @@ Containers::Array<ImageInfo> imageInfo(AbstractImporter& importer, bool& error,
image->isCompressed() ? image->isCompressed() ?
image->compressedFormat() : CompressedPixelFormat{}, image->compressedFormat() : CompressedPixelFormat{},
Vector3i::pad(image->size()), Vector3i::pad(image->size()),
image->data().size(),
j ? "" : name); j ? "" : name);
} }
} }
for(UnsignedInt i = 0; i != importer.image3DCount(); ++i) { for(UnsignedInt i = 0; i != importer.image3DCount(); ++i) {
const std::string name = importer.image3DName(i); const std::string name = importer.image3DName(i);
const UnsignedInt levelCount = importer.image3DLevelCount(i); const UnsignedInt levelCount = importer.image3DLevelCount(i);
if(!name.empty() || levelCount != 1) compact = false;
for(UnsignedInt j = 0; j != levelCount; ++j) { for(UnsignedInt j = 0; j != levelCount; ++j) {
Containers::Optional<Trade::ImageData3D> image; Containers::Optional<Trade::ImageData3D> image;
@ -128,6 +128,7 @@ Containers::Array<ImageInfo> imageInfo(AbstractImporter& importer, bool& error,
image->isCompressed() ? image->isCompressed() ?
image->compressedFormat() : CompressedPixelFormat{}, image->compressedFormat() : CompressedPixelFormat{},
image->size(), image->size(),
image->data().size(),
j ? "" : name); j ? "" : name);
} }
} }

43
src/Magnum/Trade/imageconverter.cpp

@ -32,6 +32,7 @@
#include <Corrade/Utility/Algorithms.h> #include <Corrade/Utility/Algorithms.h>
#include <Corrade/Utility/ConfigurationGroup.h> #include <Corrade/Utility/ConfigurationGroup.h>
#include <Corrade/Utility/DebugStl.h> #include <Corrade/Utility/DebugStl.h>
#include <Corrade/Utility/Format.h>
#include <Corrade/Utility/Path.h> #include <Corrade/Utility/Path.h>
#include "Magnum/ImageView.h" #include "Magnum/ImageView.h"
@ -72,7 +73,7 @@ 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] [-v|--verbose] [--profile] [--] input output [--info] [--color on|off|auto] [-v|--verbose] [--profile] [--] input output
@endcode @endcode
Arguments: Arguments:
@ -102,6 +103,7 @@ Arguments:
- `--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` --- print info about the input file and exit - `--info` --- print info about the input file and exit
- `--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
- `--profile` --- measure import and conversion time - `--profile` --- measure import and conversion time
@ -287,6 +289,7 @@ int main(int argc, char** argv) {
.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").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")
.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) {
@ -497,29 +500,43 @@ key=true; configuration subgroups are delimited with /.)")
} }
/* Parse everything first to avoid errors interleaved with /* Parse everything first to avoid errors interleaved with
output. In case the images have all just a single level and output */
no names, write them in a compact way without listing bool error = false;
levels. */
bool error = false, compact = true;
Containers::Array<Trade::Implementation::ImageInfo> infos = Containers::Array<Trade::Implementation::ImageInfo> infos =
Trade::Implementation::imageInfo(*importer, error, compact, importTime); Trade::Implementation::imageInfo(*importer, error, importTime);
/* Colored output. Enable only if a TTY. */
Debug::Flags useColor;
if(args.value("color") == "on")
useColor = Debug::Flags{};
else if(args.value("color") == "off")
useColor = Debug::Flag::DisableColors;
else
useColor = Debug::isTty() ? Debug::Flags{} : Debug::Flag::DisableColors;
for(const Trade::Implementation::ImageInfo& info: infos) { for(const Trade::Implementation::ImageInfo& info: infos) {
Debug d; Debug d{useColor};
if(info.level == 0) { if(info.level == 0) {
d << Debug::boldColor(Debug::Color::White);
if(info.size.z()) d << "3D image"; if(info.size.z()) d << "3D image";
else if(info.size.y()) d << "2D image"; else if(info.size.y()) d << "2D image";
else d << "1D image"; else d << "1D image";
d << info.image << Debug::nospace << ":"; d << info.image << Debug::nospace << ":"
if(!info.name.empty()) d << info.name; << Debug::resetColor;
if(!compact) d << Debug::newline; if(!info.name.empty()) d << Debug::boldColor(Debug::Color::Yellow)
<< info.name << Debug::resetColor;
d << Debug::newline;
} }
if(!compact) d << " Level" << info.level << Debug::nospace << ":"; d << " Level" << info.level << Debug::nospace << ":"
if(info.compressed) d << info.compressedFormat; << Debug::packed;
else d << info.format;
if(info.size.z()) d << info.size; if(info.size.z()) d << info.size;
else if(info.size.y()) d << info.size.xy(); else if(info.size.y()) d << info.size.xy();
else d << Math::Vector<1, Int>(info.size.x()); else d << Math::Vector<1, Int>(info.size.x());
d << Debug::color(Debug::Color::Blue) << "@" << Debug::resetColor;
d << Debug::packed;
if(info.compressed) d << Debug::color(Debug::Color::Yellow) << info.compressedFormat;
else d << Debug::color(Debug::Color::Cyan) << info.format;
d << Debug::resetColor << "(" << Debug::nospace << Utility::format("{:.1f}", info.dataSize/1024.0f) << "kB)";
} }
if(args.isSet("profile")) { if(args.isSet("profile")) {

Loading…
Cancel
Save