| // Copyright 2023 Google LLC | |
| // SPDX-License-Identifier: BSD-2-Clause | |
| #ifndef LIBAVIF_APPS_AVIFGAINMAPUTIL_PROGRAM_COMMAND_H_ | |
| #define LIBAVIF_APPS_AVIFGAINMAPUTIL_PROGRAM_COMMAND_H_ | |
| #include <string> | |
| #include "argparse.hpp" | |
| #include "avif/avif.h" | |
| namespace avif { | |
| // A command that can be invoked by name (similar to how 'git' has commands like | |
| // 'commit', 'checkout', etc.) | |
| // NOTE: "avifgainmaputil" is currently hardcoded in the implementation (for | |
| // help messages). | |
| class ProgramCommand { | |
| public: | |
| // 'name' is the command that should be used to invoke the command on the | |
| // command line. | |
| // 'short_description' should be a one line description of what the command | |
| // does. | |
| ProgramCommand(const std::string& name, const std::string& short_description, | |
| const std::string& long_description = ""); | |
| virtual ~ProgramCommand() = default; | |
| // Parses command line arguments. Should be called before Run(). | |
| avifResult ParseArgs(int argc, const char* const argv[]); | |
| // Runs the command. | |
| virtual avifResult Run() = 0; | |
| std::string name() const { return name_; } | |
| std::string short_description() const { return short_description_; } | |
| // Prints this command's help on stdout. | |
| void PrintUsage(); | |
| protected: | |
| argparse::ArgumentParser argparse_; | |
| private: | |
| std::string name_; | |
| std::string short_description_; | |
| }; | |
| //------------------------------------------------------------------------------ | |
| // Utilities for flag parsing. | |
| // avifPixelFormat converter for use with argparse. | |
| // Actually converts to int, converting to avifPixelFormat didn't seem to | |
| // compile. | |
| struct PixelFormatConverter { | |
| // Methods expected by argparse. | |
| argparse::ConvertedValue<int> from_str(const std::string& str); | |
| std::vector<std::string> default_choices(); | |
| }; | |
| struct CicpValues { | |
| avifColorPrimaries color_primaries; | |
| avifTransferCharacteristics transfer_characteristics; | |
| avifMatrixCoefficients matrix_coefficients; | |
| }; | |
| // CicpValues converter for use with argparse. Parses the format 'P/T/M'. | |
| struct CicpConverter { | |
| // Methods expected by argparse. | |
| argparse::ConvertedValue<CicpValues> from_str(const std::string& str); | |
| std::vector<std::string> default_choices(); | |
| }; | |
| // avifContentLightLevelInformationBox converter for use with argparse. | |
| // Parses the format 'maxCLL,maxPALL'. | |
| struct ClliConverter { | |
| // Methods expected by argparse. | |
| argparse::ConvertedValue<avifContentLightLevelInformationBox> from_str( | |
| const std::string& str); | |
| std::vector<std::string> default_choices(); | |
| }; | |
| struct GridOptions { | |
| int grid_cols = 0; | |
| int grid_rows = 0; | |
| }; | |
| // GridOptions converter for use with argparse. Parses the format 'MxN'. | |
| struct GridOptionsConverter { | |
| // Methods expected by argparse. | |
| argparse::ConvertedValue<GridOptions> from_str(const std::string& str); | |
| std::vector<std::string> default_choices(); | |
| }; | |
| // Jobs converter for use with argparse. Parses an integer or "all". | |
| struct JobsConverter { | |
| // Methods expected by argparse. | |
| argparse::ConvertedValue<int> from_str(const std::string& str); | |
| std::vector<std::string> default_choices(); | |
| }; | |
| // Basic flags for image writing. | |
| struct BasicImageEncodeArgs { | |
| argparse::ArgValue<int> speed; | |
| argparse::ArgValue<int> quality; | |
| argparse::ArgValue<int> quality_alpha; | |
| argparse::ArgValue<GridOptions> grid; | |
| // can_have_alpha should be true if the image can have alpha and the | |
| // output format can be avif. | |
| void Init(argparse::ArgumentParser& argparse, bool can_have_alpha) { | |
| argparse.add_argument(speed, "--speed", "-s") | |
| .help("Encoder speed (0-10, slowest-fastest)") | |
| .default_value("6"); | |
| argparse.add_argument(quality, "--qcolor", "-q") | |
| .help((can_have_alpha | |
| ? "Quality for color (0-100, where 100 is lossless)" | |
| : "Quality (0-100, where 100 is lossless)")) | |
| .default_value("60"); | |
| if (can_have_alpha) { | |
| argparse.add_argument(quality_alpha, "--qalpha") | |
| .help("Quality for alpha (0-100, where 100 is lossless)") | |
| .default_value("100"); | |
| } | |
| argparse.add_argument<GridOptions, GridOptionsConverter>(grid, "--grid") | |
| .help("Encode a grid AVIF with M cols and N rows, expressed as MxN") | |
| .default_value("1x1"); | |
| } | |
| }; | |
| // Flags relevant when reading jpeg/png. | |
| struct ImageReadArgs { | |
| argparse::ArgValue<int> depth; | |
| argparse::ArgValue<int> pixel_format; | |
| argparse::ArgValue<bool> ignore_profile; | |
| argparse::ArgValue<bool> ignore_alpha; | |
| argparse::ArgValue<bool> ignore_exif; | |
| argparse::ArgValue<bool> ignore_xmp; | |
| void Init(argparse::ArgumentParser& argparse) { | |
| argparse | |
| .add_argument<int, PixelFormatConverter>(pixel_format, "--yuv", "-y") | |
| .help("Output YUV format for avif (default = automatic)"); | |
| argparse.add_argument(depth, "--depth", "-d") | |
| .choices({"0", "8", "10", "12"}) | |
| .help("Output depth (0 = automatic)"); | |
| argparse.add_argument(ignore_profile, "--ignore-profile") | |
| .help( | |
| "If the input file contains an embedded color profile, ignore it " | |
| "(no-op if absent)") | |
| .action(argparse::Action::STORE_TRUE) | |
| .default_value("false"); | |
| argparse.add_argument(ignore_alpha, "--ignore-alpha") | |
| .help( | |
| "If the input file contains an alpha channel, ignore it " | |
| "(no-op if absent)") | |
| .action(argparse::Action::STORE_TRUE) | |
| .default_value("false"); | |
| argparse.add_argument(ignore_exif, "--ignore-exif") | |
| .help( | |
| "If the input file contains embedded Exif metadata, ignore it " | |
| "(no-op if absent)") | |
| .action(argparse::Action::STORE_TRUE) | |
| .default_value("false"); | |
| argparse.add_argument(ignore_xmp, "--ignore-xmp") | |
| .help( | |
| "If the input file contains embedded XMP metadata, ignore it " | |
| "(no-op if absent)") | |
| .action(argparse::Action::STORE_TRUE) | |
| .default_value("false"); | |
| } | |
| }; | |
| // Flags for setting the number of worker threads. | |
| struct JobsArgs { | |
| argparse::ArgValue<int> jobs; | |
| void Init(argparse::ArgumentParser& argparse) { | |
| argparse.add_argument<int, JobsConverter>(jobs, "--jobs", "-j") | |
| .help( | |
| "Number of jobs (worker threads), or 'all' to potentially use as " | |
| "many cores as possible.") | |
| .default_value("all"); | |
| } | |
| }; | |
| // Helper to parse flags that contain several delimited values. | |
| template <typename T> | |
| bool ParseList(std::string to_parse, char delim, int expected_num, | |
| std::vector<T>* out) { | |
| std::stringstream ss(to_parse); | |
| std::string part; | |
| T parsed; | |
| while (std::getline(ss, part, delim)) { | |
| std::istringstream is(part); | |
| is >> parsed; | |
| if (is.bad()) { | |
| return false; | |
| } | |
| out->push_back(parsed); | |
| } | |
| if (expected_num > 0 && (int)out->size() != expected_num) { | |
| return false; | |
| } | |
| return true; | |
| } | |
| } // namespace avif | |
| #endif // LIBAVIF_APPS_AVIFGAINMAPUTIL_PROGRAM_COMMAND_H_ |