Skip to content

Commit

Permalink
KTXwriterScParams support (#779)
Browse files Browse the repository at this point in the history
Fixes #758.

- Remove unneeded astc-mode option from `ktx create`.

Depends on the CTS PR
KhronosGroup/KTX-Software-CTS#5.
  • Loading branch information
aqnuep authored Oct 5, 2023
1 parent 9a1ccbe commit f8691ff
Show file tree
Hide file tree
Showing 6 changed files with 307 additions and 190 deletions.
2 changes: 1 addition & 1 deletion tests/cts
Submodule cts updated 644 files
230 changes: 138 additions & 92 deletions tools/ktx/command_create.cpp

Large diffs are not rendered by default.

10 changes: 10 additions & 0 deletions tools/ktx/command_encode.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,16 @@ void CommandEncode::executeEncode() {
fatal(rc::IO_FAILURE, "ZLIB deflation failed. KTX Error: {}", ktxErrorString(ret));
}

// Add KTXwriterScParams metadata
const auto writerScParams = fmt::format("{}{}", options.codecOptions, options.compressOptions);
if (writerScParams.size() > 0) {
// Options always contain a leading space
assert(writerScParams[0] == ' ');
ktxHashList_AddKVPair(&texture->kvDataHead, KTX_WRITER_SCPARAMS_KEY,
static_cast<uint32_t>(writerScParams.size()),
writerScParams.c_str() + 1); // +1 to exclude leading space
}

// Save output file
if (std::filesystem::path(options.outputFilepath).has_parent_path())
std::filesystem::create_directories(std::filesystem::path(options.outputFilepath).parent_path());
Expand Down
55 changes: 33 additions & 22 deletions tools/ktx/command_extract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,17 @@ Extract selected images from a KTX2 file.
*/
class CommandExtract : public Command {
struct OptionsExtract {
inline static const char* kOutput = "output";
inline static const char* kStdout = "stdout";
inline static const char* kTranscode = "transcode";
inline static const char* kUri = "uri";
inline static const char* kLevel = "level";
inline static const char* kLayer = "layer";
inline static const char* kFace = "face";
inline static const char* kDepth = "depth";
inline static const char* kAll = "all";
inline static const char* kRaw = "raw";

std::string outputPath;
FragmentURI fragmentURI;
SelectorRange depth;
Expand Down Expand Up @@ -180,9 +191,9 @@ int CommandExtract::main(int argc, _TCHAR* argv[]) {

void CommandExtract::OptionsExtract::init(cxxopts::Options& opts) {
opts.add_options()
("output", "Output filepath for single, output directory for multiple image export.", cxxopts::value<std::string>(), "<filepath>")
("stdout", "Use stdout as the output file. (Using a single dash '-' as the output file has the same effect)")
("transcode", "Transcode the texture to the target format before executing the extract steps."
(kOutput, "Output filepath for single, output directory for multiple image export.", cxxopts::value<std::string>(), "<filepath>")
(kStdout, "Use stdout as the output file. (Using a single dash '-' as the output file has the same effect)")
(kTranscode, "Transcode the texture to the target format before executing the extract steps."
" Requires the input file to be transcodable."
" Block compressed transcode targets can only be saved in raw format."
" Case-insensitive."
Expand All @@ -191,18 +202,18 @@ void CommandExtract::OptionsExtract::init(cxxopts::Options& opts) {
" r8 | rg8 | rgb8 | rgba8."
"\netc-rgb is ETC1; etc-rgba, eac-r11 and eac-rg11 are ETC2.",
cxxopts::value<std::string>(), "<target>")
("uri", "KTX Fragment URI.", cxxopts::value<std::string>(), "<uri>")
("level", "Level to extract. When 'all' is used every level is exported. Defaults to 0.", cxxopts::value<std::string>(), "[0-9]+ | all")
("layer", "Layer to extract. When 'all' is used every layer is exported. Defaults to 0.", cxxopts::value<std::string>(), "[0-9]+ | all")
("face", "Face to extract. When 'all' is used every face is exported. Defaults to 0.", cxxopts::value<std::string>(), "[0-5] | all")
("depth", "Depth slice to extract. When 'all' is used every depth is exported. Defaults to 0.", cxxopts::value<std::string>(), "[0-9]+ | all")
("all", "Extract every image slice from the texture.")
("raw", "Extract the raw image data without any conversion.");
(kUri, "KTX Fragment URI.", cxxopts::value<std::string>(), "<uri>")
(kLevel, "Level to extract. When 'all' is used every level is exported. Defaults to 0.", cxxopts::value<std::string>(), "[0-9]+ | all")
(kLayer, "Layer to extract. When 'all' is used every layer is exported. Defaults to 0.", cxxopts::value<std::string>(), "[0-9]+ | all")
(kFace, "Face to extract. When 'all' is used every face is exported. Defaults to 0.", cxxopts::value<std::string>(), "[0-5] | all")
(kDepth, "Depth slice to extract. When 'all' is used every depth is exported. Defaults to 0.", cxxopts::value<std::string>(), "[0-9]+ | all")
(kAll, "Extract every image slice from the texture.")
(kRaw, "Extract the raw image data without any conversion.");
}

void CommandExtract::OptionsExtract::process(cxxopts::Options&, cxxopts::ParseResult& args, Reporter& report) {
if (args.count("output"))
outputPath = args["output"].as<std::string>();
if (args.count(kOutput))
outputPath = args[kOutput].as<std::string>();
else
report.fatal_usage("Missing output file or directory path.");

Expand All @@ -212,7 +223,7 @@ void CommandExtract::OptionsExtract::process(cxxopts::Options&, cxxopts::ParseRe
const auto str = to_lower_copy(args[name].as<std::string>());
try {
found = true;
return str == "all" ? SelectorRange(all) : SelectorRange(std::stoi(str));
return str == kAll ? SelectorRange(all) : SelectorRange(std::stoi(str));
} catch (const std::invalid_argument&) {
report.fatal_usage("Invalid {} value \"{}\". The value must be a either a number or \"all\".", name, str);
} catch (const std::out_of_range& e) {
Expand All @@ -221,12 +232,12 @@ void CommandExtract::OptionsExtract::process(cxxopts::Options&, cxxopts::ParseRe
return std::nullopt;
};

auto level = parseSelector("level", levelFlagUsed);
auto layer = parseSelector("layer", layerFlagUsed);
auto face = parseSelector("face", faceFlagUsed);
auto depth_ = parseSelector("depth", depthFlagUsed);
raw = args["raw"].as<bool>();
globalAll = args["all"].as<bool>();
auto level = parseSelector(kLevel, levelFlagUsed);
auto layer = parseSelector(kLayer, layerFlagUsed);
auto face = parseSelector(kFace, faceFlagUsed);
auto depth_ = parseSelector(kDepth, depthFlagUsed);
raw = args[kRaw].as<bool>();
globalAll = args[kAll].as<bool>();

if (globalAll) {
if (level)
Expand Down Expand Up @@ -255,7 +266,7 @@ void CommandExtract::OptionsExtract::process(cxxopts::Options&, cxxopts::ParseRe
if (depth_ == all && outputPath == "-")
report.fatal_usage("stdout cannot be used with multi-output '--depth all' extract.");

if (args["uri"].count()) {
if (args[kUri].count()) {
uriFlagUsed = true;

if (globalAll)
Expand All @@ -268,7 +279,7 @@ void CommandExtract::OptionsExtract::process(cxxopts::Options&, cxxopts::ParseRe
report.fatal_usage("Conflicting options: --face cannot be used with --uri.");

try {
fragmentURI = parseFragmentURI(args["uri"].as<std::string>());
fragmentURI = parseFragmentURI(args[kUri].as<std::string>());
} catch (const std::exception& e) {
report.fatal_usage("Failed to parse Fragment URI: {}", e.what());
}
Expand All @@ -292,7 +303,7 @@ void CommandExtract::OptionsExtract::process(cxxopts::Options&, cxxopts::ParseRe

void CommandExtract::initOptions(cxxopts::Options& opts) {
options.init(opts);
opts.parse_positional({"input-file", "output"});
opts.parse_positional({"input-file", OptionsExtract::kOutput});
opts.positional_help("<input-file> <output>");
}

Expand Down
23 changes: 17 additions & 6 deletions tools/ktx/compress_utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,32 +33,43 @@ namespace ktx {
//! [command options_compress]
*/
struct OptionsCompress {
inline static const char* kZStd = "zstd";
inline static const char* kZLib = "zlib";

std::string compressOptions{};
std::optional<uint32_t> zstd;
std::optional<uint32_t> zlib;

void init(cxxopts::Options& opts) {
opts.add_options()
("zstd", "Supercompress the data with Zstandard."
(kZStd, "Supercompress the data with Zstandard."
" Cannot be used with ETC1S / BasisLZ format."
" Level range is [1,22]."
" Lower levels give faster but worse compression."
" Values above 20 should be used with caution as they require more memory.",
cxxopts::value<uint32_t>(), "<level>")
("zlib", "Supercompress the data with ZLIB."
(kZLib, "Supercompress the data with ZLIB."
" Cannot be used with ETC1S / BasisLZ format."
" Level range is [1,9]."
" Lower levels give faster but worse compression.",
cxxopts::value<uint32_t>(), "<level>");
}

template <typename T>
T captureCompressOption(cxxopts::ParseResult& args, const char* name) {
const T value = args[name].as<T>();
compressOptions += fmt::format(" --{} {}", name, value);
return value;
}

void process(cxxopts::Options&, cxxopts::ParseResult& args, Reporter& report) {
if (args["zstd"].count()) {
zstd = args["zstd"].as<uint32_t>();
if (args[kZStd].count()) {
zstd = captureCompressOption<uint32_t>(args, kZStd);
if (zstd < 1u || zstd > 22u)
report.fatal_usage("Invalid zstd level: \"{}\". Value must be between 1 and 22 inclusive.", zstd.value());
}
if (args["zlib"].count()) {
zlib = args["zlib"].as<uint32_t>();
if (args[kZLib].count()) {
zlib = captureCompressOption<uint32_t>(args, kZLib);
if (zlib < 1u || zlib > 9u)
report.fatal_usage("Invalid zlib level: \"{}\". Value must be between 1 and 9 inclusive.", zlib.value());
}
Expand Down
Loading

0 comments on commit f8691ff

Please sign in to comment.