From 5d4342f1c12fdfb337fdb1b24bc269a3480cac64 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Vladim=C3=ADr=20Vondru=C5=A1?= Date: Sun, 22 Aug 2021 13:23:17 +0200 Subject: [PATCH] imageconverter: implicitly import and convert all image levels. In case of --layers and --levels this only works if the input images have a single level, otherwise --level has to be set. The internal implementation would be too complex otherwise. As a consequence, combining a set of 2D mipmapped images into a 3D mipmapped image means one first has to combine particular 2D image levels to 3D levels and then combine all 3D levels to a 3D mipmapped image, it can't be done in a single step and it also can't be done by first combining levels and then layers. --- src/Magnum/Trade/imageconverter.cpp | 117 ++++++++++++++++++---------- 1 file changed, 74 insertions(+), 43 deletions(-) diff --git a/src/Magnum/Trade/imageconverter.cpp b/src/Magnum/Trade/imageconverter.cpp index e9dccbeaa..146a5c366 100644 --- a/src/Magnum/Trade/imageconverter.cpp +++ b/src/Magnum/Trade/imageconverter.cpp @@ -94,7 +94,7 @@ Arguments: - `-D`, `--dimensions N` --- import and convert image of given dimensions (default: `2`) - `--image N` --- image to import (default: `0`) -- `--level N` --- image level to import (default: `0`) +- `--level N` --- import given image level instead of all - `--layers` --- combine multiple layers into an image with one dimension more - `--levels` --- combine multiple image levels into a single file @@ -234,7 +234,7 @@ int main(int argc, char** argv) { .addOption('c', "converter-options").setHelp("converter-options", "configuration options to pass to the converter", "key=val,key2=val2,…") .addOption('D', "dimensions", "2").setHelp("dimensions", "import and convert image of given dimensions", "N") .addOption("image", "0").setHelp("image", "image to import", "N") - .addOption("level", "0").setHelp("level", "image level to import", "N") + .addOption("level").setHelp("level", "import given image level instead of all", "N") .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("in-place").setHelp("in-place", "overwrite the input image with the output") @@ -309,7 +309,8 @@ key=true; configuration subgroups are delimited with /.)") const Int dimensions = args.value("dimensions"); /** @todo make them array options as well? */ const UnsignedInt image = args.value("image"); - const UnsignedInt level = args.value("level"); + Containers::Optional level; + if(!args.value("level").empty()) level = args.value("level"); Containers::Array images1D; Containers::Array images2D; Containers::Array images3D; @@ -434,14 +435,29 @@ key=true; configuration subgroups are delimited with /.)") Error{} << "1D image number" << image << "not found in" << input << Debug::nospace << ", the file has only" << importer->image1DCount() << "1D images"; return 1; } - if(level >= importer->image1DLevelCount(image)) { - Error{} << "1D image" << image << "in" << input << "doesn't have a level number" << level << Debug::nospace << ", only" << importer->image1DLevelCount(image) << "levels"; - return 1; - } - if(Containers::Optional image1D = importer->image1D(image, level)) { - arrayAppend(images1D, std::move(*image1D)); - imported = true; + /* Import all levels of the input or just one if specified */ + UnsignedInt minLevel, maxLevel; + if(level) { + minLevel = *level; + maxLevel = *level + 1; + if(*level >= importer->image1DLevelCount(image)) { + Error{} << "1D image" << image << "in" << input << "doesn't have a level number" << level << Debug::nospace << ", only" << importer->image1DLevelCount(image) << "levels"; + return 1; + } + } else { + minLevel = 0; + maxLevel = importer->image1DLevelCount(image); + if(maxLevel > 1 && (args.isSet("layers") || args.isSet("levels") || args.value("converter") == "raw")) { + Error{} << "Cannot use --layers / --levels or raw output with multi-level input images. Specify --level N to extract just one level from each."; + return 1; + } + } + for(; minLevel != maxLevel; ++minLevel) { + if(Containers::Optional image1D = importer->image1D(image, minLevel)) { + arrayAppend(images1D, std::move(*image1D)); + imported = true; + } } } else if(dimensions == 2) { @@ -453,14 +469,29 @@ key=true; configuration subgroups are delimited with /.)") Error{} << "2D image number" << image << "not found in" << input << Debug::nospace << ", the file has only" << importer->image2DCount() << "2D images"; return 1; } - if(level >= importer->image2DLevelCount(image)) { - Error{} << "2D image" << image << "in" << input << "doesn't have a level number" << level << Debug::nospace << ", only" << importer->image2DLevelCount(image) << "levels"; - return 1; - } - if(Containers::Optional image2D = importer->image2D(image, level)) { - arrayAppend(images2D, std::move(*image2D)); - imported = true; + /* Import all levels of the input or just one if specified */ + UnsignedInt minLevel, maxLevel; + if(level) { + minLevel = *level; + maxLevel = *level + 1; + if(*level >= importer->image2DLevelCount(image)) { + Error{} << "2D image" << image << "in" << input << "doesn't have a level number" << level << Debug::nospace << ", only" << importer->image2DLevelCount(image) << "levels"; + return 1; + } + } else { + minLevel = 0; + maxLevel = importer->image2DLevelCount(image); + if(maxLevel > 1 && (args.isSet("layers") || args.isSet("levels") || args.value("converter") == "raw")) { + Error{} << "Cannot use --layers / --levels or raw output with multi-level input images. Specify --level N to extract just one level from each."; + return 1; + } + } + for(; minLevel != maxLevel; ++minLevel) { + if(Containers::Optional image2D = importer->image2D(image, minLevel)) { + arrayAppend(images2D, std::move(*image2D)); + imported = true; + } } } else if(dimensions == 3) { @@ -472,14 +503,29 @@ key=true; configuration subgroups are delimited with /.)") Error{} << "3D image number" << image << "not found in" << input << Debug::nospace << ", the file has only" << importer->image3DCount() << "3D images"; return 1; } - if(level >= importer->image3DLevelCount(image)) { - Error{} << "3D image" << image << "in" << input << "doesn't have a level number" << level << Debug::nospace << ", only" << importer->image3DLevelCount(image) << "levels"; - return 1; - } - if(Containers::Optional image3D = importer->image3D(image, level)) { - arrayAppend(images3D, std::move(*image3D)); - imported = true; + /* Import all levels of the input or just one if specified */ + UnsignedInt minLevel, maxLevel; + if(level) { + minLevel = *level; + maxLevel = *level + 1; + if(*level >= importer->image3DLevelCount(image)) { + Error{} << "3D image" << image << "in" << input << "doesn't have a level number" << level << Debug::nospace << ", only" << importer->image3DLevelCount(image) << "levels"; + return 1; + } + } else { + minLevel = 0; + maxLevel = importer->image3DLevelCount(image); + if(maxLevel > 1 && (args.isSet("layers") || args.isSet("levels") || args.value("converter") == "raw")) { + Error{} << "Cannot use --layers / --levels or raw output with multi-level input images. Specify --level N to extract just one level from each."; + return 1; + } + } + for(; minLevel != maxLevel; ++minLevel) { + if(Containers::Optional image3D = importer->image3D(image, minLevel)) { + arrayAppend(images3D, std::move(*image3D)); + imported = true; + } } } else { @@ -570,9 +616,10 @@ key=true; configuration subgroups are delimited with /.)") } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); - /* Multi-level conversion, verify that all have the same format and pass - the input through */ - } else if(args.isSet("levels")) { + /* Single-image (potentially multi-level) conversion, verify that all have + the same format and pass the input through. This happens either if + --levels is set or if the (single) input image is multi-level. */ + } else { if(dimensions == 1) { if(!checkCommonFormat(args, images1D)) return 1; outputDimensions = 1; @@ -586,22 +633,6 @@ key=true; configuration subgroups are delimited with /.)") outputDimensions = 3; outputImages3D = std::move(images3D); } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); - - /* Single image conversion, just pass the input through */ - } else { - if(dimensions == 1) { - CORRADE_INTERNAL_ASSERT(images1D.size() == 1); - outputDimensions = 1; - arrayAppend(outputImages1D, std::move(images1D.front())); - } else if(dimensions == 2) { - CORRADE_INTERNAL_ASSERT(images2D.size() == 1); - outputDimensions = 2; - arrayAppend(outputImages2D, std::move(images2D.front())); - } else if(dimensions == 3) { - CORRADE_INTERNAL_ASSERT(images3D.size() == 1); - outputDimensions = 3; - arrayAppend(outputImages3D, std::move(images3D.front())); - } else CORRADE_INTERNAL_ASSERT_UNREACHABLE(); } {