From 9926111e1cc92b6e63e38a7139cc61bccf0e356b Mon Sep 17 00:00:00 2001 From: Chris Blume Date: Wed, 14 May 2025 13:13:25 -0400 Subject: [PATCH] Use std::variant for actions --- Code/png_cicp_editor/Actions.cpp | 808 +++++------- Code/png_cicp_editor/Actions.hpp | 149 +-- Code/png_cicp_editor/CICPCreator.cpp | 196 +-- .../png_cicp_editor/CommandLineParameters.cpp | 1142 +++++++++-------- .../png_cicp_editor/CommandLineParameters.hpp | 66 +- Code/png_cicp_editor/EntryPoint.cpp | 57 +- Code/tests/ActionsTest.cpp | 114 +- Code/tests/CommandLineParametersTest.cpp | 1142 +++++++++-------- 8 files changed, 1756 insertions(+), 1918 deletions(-) diff --git a/Code/png_cicp_editor/Actions.cpp b/Code/png_cicp_editor/Actions.cpp index b01905d..b4de20e 100644 --- a/Code/png_cicp_editor/Actions.cpp +++ b/Code/png_cicp_editor/Actions.cpp @@ -1,487 +1,321 @@ -// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "Actions.hpp" - -#include -#include - -#include - -#include "CICPCreator.hpp" -#include "CICPInserter.hpp" -#include "FileReader.hpp" -#include "FileWriter.hpp" -#include "PNGParser.hpp" - -namespace { - - void print_bsd_3_clause_license(const std::string_view year, const std::string_view copyright_holder) noexcept { - std::cout << "Copyright " << year << ", " << copyright_holder << R"( -All rights reserved. - -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: - - * Redistributions of source code must retain the above copyright -notice, this list of conditions and the following disclaimer. - * Redistributions in binary form must reproduce the above -copyright notice, this list of conditions and the following disclaimer -in the documentation and/or other materials provided with the -distribution. - * Neither the name of copyright holder nor the names of its -contributors may be used to endorse or promote products derived from -this software without specific prior written permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -)"; - } - -} // anonymous namespace - -namespace PNG_CICP_Editor { - - void VersionAction::operator()() const noexcept { - std::cout << "png_cicp_editor version 3.0" << std::endl; - } - - void HelpAction::operator()() const noexcept { -#if defined(MAX_PLATFORM_WINDOWS) - static constinit auto program_name = std::string_view{ "png_cicp_editor.exe" }; - static constinit auto file_path = std::string_view{ R"(C:\images\test.png)" }; -#elif defined(MAX_PLATFORM_LINUX) || defined(MAX_PLATFORM_MACOS) - static constinit auto program_name = std::string_view{ "png_cicp_editor" }; - static constinit auto file_path = std::string_view{ R"(/images/test.png)" }; -#else - static_assert( false, "Unknown platform" ); -#endif - - // Print version - auto version_action = VersionAction{}; - version_action(); - - std::cout << - R"(This program enabled CICP editing within a PNG file. -CICP is an efficient way to specify color space. -It is standardized in ITU-T H.273, which can be found here: -https://www.itu.int/rec/T-REC-H.273 - -Example usage: )" << program_name << R"( add --preset display-p3 )" << file_path << R"( - -Presets: - bt.601-pal Rec. ITU-R BT.601 625-line 50 Hz (PAL) - bt.601-ntsc Rec. ITU-R BT.601 525-line 60 Hz (NTSC) - bt.709 Rec. ITU-R BT.709-6 - srgb-linear linear-light sRGB - srgb IEC 61966-2-1 sRGB - bt.2020-10-bit Rec. ITU-R BT.2020-2 (10-bit system) - bt.2020-12-bit Rec. ITU-R BT.2020-2 (12-bit system) - bt.2100-pq Rec. ITU-R BT.2100-2 perceptual quantization (PQ) system - bt.2100-hlg Rec. ITU-R BT.2100-2 hybrid log-gamma (HLG) system - dci-p3 SMPTE RP 431-2 with SMPTE ST 428-1 D-Cinema Distribution Master (DCI-P3) - display-p3 Display P3 - p3-d65-pq P3-D65 PQ - -You can also specify individual CICP values. For example, to label an RGB image decoded from a SECAM video: -Example usage: )" << program_name << R"( add --color_primaries 5 --transfer_function 4 --matrix_coefficients 0 --video_full_range_flag 1 )" << file_path << R"( - -These can be mixed to override defaults. Values specified later override prior values. -Example usage: )" << program_name << R"( add --preset display-p3 --video_full_range_flag 0 )" << file_path << R"( - -Actions: - -h --help Show help information (what you are viewing now) - -v --version Show version information - --license Show license information - add [flags] Adds a new cICP chunk to the PNG file - overwrite [flags] Adds a new or overwrites an existing cICP chunk - remove Removes the cICP chunk from the PNG file - -General flags: - -p --preset [value] Use [value]'s CICP values - -n --narrow Use narrow range (--video_full_range_flag 0) - -f --full Use full range (--video_full_range_flag 1) - -Specific flags to match CICP parameter names from ITU-T H.273 (experts only): - --color_primaries [value] - --transfer_function [value] - --matrix_coefficients [value] - --video_full_range_flag [value] -Note: Specific flags use values from ITU-T H.273. Not all values are valid. -PNG puts further restrictions on which values are valid. -)" << std::endl; - } - - void LicenseAction::operator()() const noexcept { - static const auto png_cicp_editor_year = std::string_view{"2025"}; - static const auto png_cicp_editor_copyright_holder = std::string_view{"The png_cicp_editor Contributors"}; - print_bsd_3_clause_license(png_cicp_editor_year, png_cicp_editor_copyright_holder); - - std::cout << R"( -png_cicp_editor depends on max: -)"; - - static const auto max_year = std::string_view{"2025"}; - static const auto max_copyright_holder = std::string_view{"The max Contributors"}; - print_bsd_3_clause_license(max_year, max_copyright_holder); - } - - AddAction::AddAction(CICP cicp, std::string file_path) noexcept - : cicp_(std::move(cicp)) - , file_path_(std::move(file_path)) - {} - - void AddAction::operator()() const noexcept { - // TODO: Return error values - // Read the file - auto file_contents = read_file(file_path_); - if (!file_contents.has_value()) { - print_error(file_contents.error()); - //return 1; - return; - } - - - // Get indicies of all chunks - auto chunk_indices = get_chunk_indices(file_contents.value()); - if (!chunk_indices.has_value()) { - print_error(chunk_indices.error()); - //return 1; - return; - } - - - // Find overwrite index for cICP chunk - auto split_buffer = get_split_buffer_across_cicp_insertion_point(file_contents.value(), chunk_indices.value(), /*overwrite_cicp*/ false); - if (!split_buffer.has_value()) { - print_error(split_buffer.error()); - //return 1; - return; - } - - - // Prepare cICP buffer to write - auto cicp_buffer = create_cicp_buffer(cicp_); - - - // Prepare file before & after buffers for write - std::vector> buffers; - buffers.push_back({ split_buffer.value()[0] }); - buffers.push_back({ cicp_buffer }); - buffers.push_back({ split_buffer.value()[1] }); - - - // Write the file with cICP inserted - auto write_result = PNG_CICP_Editor::write_file(file_path_, buffers); - if (!write_result.has_value()) { - print_error(write_result.error()); - //return 1; - return; - } - } - - OverwriteAction::OverwriteAction(CICP cicp, std::string file_path) noexcept - : cicp_(std::move(cicp)) - , file_path_(std::move(file_path)) - {} - - void OverwriteAction::operator()() const noexcept { - // TODO: Return error values - // Read the file - auto file_contents = read_file(file_path_); - if (!file_contents.has_value()) { - print_error(file_contents.error()); - //return 1; - return; - } - - - // Get indicies of all chunks - auto chunk_indices = get_chunk_indices(file_contents.value()); - if (!chunk_indices.has_value()) { - print_error(chunk_indices.error()); - //return 1; - return; - } - - - // Find overwrite index for cICP chunk - auto split_buffer = get_split_buffer_across_cicp_insertion_point(file_contents.value(), chunk_indices.value(), /*overwrite_cicp*/ true); - if (!split_buffer.has_value()) { - print_error(split_buffer.error()); - //return 1; - return; - } - - - // Prepare cICP buffer to write - auto cicp_buffer = create_cicp_buffer(cicp_); - - - // Prepare file before & after buffers for write - std::vector> buffers; - buffers.push_back({ split_buffer.value()[0] }); - buffers.push_back({ cicp_buffer }); - buffers.push_back({ split_buffer.value()[1] }); - - - // Write the file with cICP inserted - auto write_result = PNG_CICP_Editor::write_file(file_path_, buffers); - if (!write_result.has_value()) { - print_error(write_result.error()); - //return 1; - return; - } - } - - RemoveAction::RemoveAction(std::string file_path) noexcept - : file_path_(std::move(file_path)) - {} - - void RemoveAction::operator()() const noexcept { - // TODO: Return error values - // Read the file - auto file_contents = read_file(file_path_); - if (!file_contents.has_value()) { - print_error(file_contents.error()); - //return 1; - return; - } - - - // Get indicies of all chunks - auto chunk_indices = get_chunk_indices(file_contents.value()); - if (!chunk_indices.has_value()) { - print_error(chunk_indices.error()); - //return 1; - return; - } - - - // TODO: Right now, this only removes the first cICP chunk found - // There might incorrectly be multiple. - auto cicp_index = size_t{ SIZE_MAX }; - auto after_cicp_index = size_t{ SIZE_MAX }; - auto chunk_count = chunk_indices->size(); - for (size_t i = 0; i < chunk_count; i++) { - auto index = (*chunk_indices)[i]; - if ((*file_contents)[index + 4] == 'c' && - (*file_contents)[index + 5] == 'I' && - (*file_contents)[index + 6] == 'C' && - (*file_contents)[index + 7] == 'P') { - cicp_index = index; - // the cICP chunk may incorrectly be the last chunk in the file, - // so there is no chunk after it - if (i < chunk_count - 1) { - after_cicp_index = (*chunk_indices)[i + 1]; - } - break; - } - } - - if (cicp_index == SIZE_MAX) { - // no cICP chunk found - print_error( Error{ { "no cICP chunk found" } } ); - return; - } - if (after_cicp_index == SIZE_MAX) { - // cICP was last chunk, which isn't allowed - print_error( Error{ { "Ill-formed file: cICP was the final chunk" } } ); - return; - } - - // Prepare file before & after buffers for write - auto file_contents_start = file_contents->data(); - std::vector> buffers; - buffers.push_back({ file_contents_start, file_contents_start + cicp_index }); - buffers.push_back({ file_contents_start + after_cicp_index, file_contents->size() - after_cicp_index }); - - - // Write the file with cICP inserted - auto write_result = PNG_CICP_Editor::write_file(file_path_, buffers); - if (!write_result.has_value()) { - print_error(write_result.error()); - //return 1; - return; - } - } - - Action::Action(VersionAction version) noexcept - : action_type_(Actions::Version) - , action_{.version_ = std::move(version)} - {} - - Action::Action(HelpAction help) noexcept - : action_type_(Actions::Help) - , action_{.help_ = std::move(help)} - {} - - Action::Action(LicenseAction license) noexcept - : action_type_(Actions::License) - , action_{.license_ = std::move(license)} - {} - - Action::Action(AddAction add) noexcept - : action_type_(Actions::Add) - , action_{.add_ = std::move(add)} - {} - - Action::Action(OverwriteAction overwrite) noexcept - : action_type_(Actions::Overwrite) - , action_{.overwrite_ = std::move(overwrite)} - {} - - Action::Action(RemoveAction remove) noexcept - : action_type_(Actions::Remove) - , action_{.remove_ = std::move(remove)} - {} - - Action::Action(const Action& rhs) noexcept - : action_type_(rhs.action_type_) - , action_{.version_{}} - { - switch (action_type_) { - case Actions::Version: - action_.version_ = rhs.action_.version_; - break; - case Actions::Help: - action_.help_ = rhs.action_.help_; - break; - case Actions::License: - action_.license_ = rhs.action_.license_; - break; - case Actions::Add: - action_.add_ = rhs.action_.add_; - break; - case Actions::Overwrite: - action_.overwrite_ = rhs.action_.overwrite_; - break; - case Actions::Remove: - action_.remove_ = rhs.action_.remove_; - break; - } - } - - Action::Action(Action&& rhs) noexcept - : action_type_(std::move(rhs.action_type_)) - , action_{.version_{}} - { - switch (action_type_) { - case Actions::Version: - new (&action_.version_) VersionAction(std::move(rhs.action_.version_)); - break; - case Actions::Help: - new (&action_.help_) HelpAction(std::move(rhs.action_.help_)); - break; - case Actions::License: - new (&action_.license_) LicenseAction(std::move(rhs.action_.license_)); - break; - case Actions::Add: - new (&action_.add_) AddAction(std::move(rhs.action_.add_)); - break; - case Actions::Overwrite: - new (&action_.overwrite_) OverwriteAction(std::move(rhs.action_.overwrite_)); - break; - case Actions::Remove: - new (&action_.remove_) RemoveAction(std::move(rhs.action_.remove_)); - break; - } - } - - - Action::~Action() noexcept { - switch (action_type_) { - case Actions::Version: - (&action_.version_)->~VersionAction(); - break; - case Actions::Help: - (&action_.help_)->~HelpAction(); - break; - case Actions::License: - (&action_.license_)->~LicenseAction(); - break; - case Actions::Add: - (&action_.add_)->~AddAction(); - break; - case Actions::Overwrite: - (&action_.overwrite_)->~OverwriteAction(); - break; - case Actions::Remove: - (&action_.remove_)->~RemoveAction(); - break; - } - } - - Action& Action::operator =(const Action& rhs) noexcept { - this->~Action(); - - action_type_ = rhs.action_type_; - - switch (rhs.action_type_) { - case Actions::Version: - action_.version_ = rhs.action_.version_; - break; - case Actions::Help: - action_.help_ = rhs.action_.help_; - break; - case Actions::License: - action_.license_ = rhs.action_.license_; - break; - case Actions::Add: - action_.add_ = rhs.action_.add_; - break; - case Actions::Overwrite: - action_.overwrite_ = rhs.action_.overwrite_; - break; - case Actions::Remove: - action_.remove_ = rhs.action_.remove_; - break; - } - - return *this; - } - - Action& Action::operator =(Action&& rhs) noexcept { - this->~Action(); - - action_type_ = std::move(rhs.action_type_); - - switch (rhs.action_type_) { - case Actions::Version: - action_.version_ = std::move(rhs.action_.version_); - break; - case Actions::Help: - action_.help_ = std::move(rhs.action_.help_); - break; - case Actions::License: - action_.license_ = std::move(rhs.action_.license_); - break; - case Actions::Add: - action_.add_ = std::move(rhs.action_.add_); - break; - case Actions::Overwrite: - action_.overwrite_ = std::move(rhs.action_.overwrite_); - break; - case Actions::Remove: - action_.remove_ = std::move(rhs.action_.remove_); - break; - } - - return *this; - } - - Action::A::~A() noexcept { - } - -} // namespace PNG_CICP_Editor +// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "Actions.hpp" + +#include +#include + +#include + +#include "CICPCreator.hpp" +#include "CICPInserter.hpp" +#include "FileReader.hpp" +#include "FileWriter.hpp" +#include "PNGParser.hpp" + +namespace { + + void print_bsd_3_clause_license(const std::string_view year, const std::string_view copyright_holder) noexcept { + std::cout << "Copyright " << year << ", " << copyright_holder << R"( +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of copyright holder nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +)"; + } + +} // anonymous namespace + +namespace PNG_CICP_Editor { + + AddAction::AddAction(CICP cicp, std::string file_path) noexcept + : cicp_(std::move(cicp)) + , file_path_(std::move(file_path)) + {} + + OverwriteAction::OverwriteAction(CICP cicp, std::string file_path) noexcept + : cicp_(std::move(cicp)) + , file_path_(std::move(file_path)) + {} + + RemoveAction::RemoveAction(std::string file_path) noexcept + : file_path_(std::move(file_path)) + {} + + void ActionExecutor::operator()(const VersionAction&) const noexcept { + std::cout << "png_cicp_editor version 3.0" << std::endl; + } + + void ActionExecutor::operator()(const HelpAction&) const noexcept { +#if defined(MAX_PLATFORM_WINDOWS) + static constinit auto program_name = std::string_view{ "png_cicp_editor.exe" }; + static constinit auto file_path = std::string_view{ R"(C:\images\test.png)" }; +#elif defined(MAX_PLATFORM_LINUX) || defined(MAX_PLATFORM_MACOS) + static constinit auto program_name = std::string_view{ "png_cicp_editor" }; + static constinit auto file_path = std::string_view{ R"(/images/test.png)" }; +#else + static_assert( false, "Unknown platform" ); +#endif + + // Print version + operator()(VersionAction{}); + + std::cout << + R"(This program enabled CICP editing within a PNG file. +CICP is an efficient way to specify color space. +It is standardized in ITU-T H.273, which can be found here: +https://www.itu.int/rec/T-REC-H.273 + +Example usage: )" << program_name << R"( add --preset display-p3 )" << file_path << R"( + +Presets: + bt.601-pal Rec. ITU-R BT.601 625-line 50 Hz (PAL) + bt.601-ntsc Rec. ITU-R BT.601 525-line 60 Hz (NTSC) + bt.709 Rec. ITU-R BT.709-6 + srgb-linear linear-light sRGB + srgb IEC 61966-2-1 sRGB + bt.2020-10-bit Rec. ITU-R BT.2020-2 (10-bit system) + bt.2020-12-bit Rec. ITU-R BT.2020-2 (12-bit system) + bt.2100-pq Rec. ITU-R BT.2100-2 perceptual quantization (PQ) system + bt.2100-hlg Rec. ITU-R BT.2100-2 hybrid log-gamma (HLG) system + dci-p3 SMPTE RP 431-2 with SMPTE ST 428-1 D-Cinema Distribution Master (DCI-P3) + display-p3 Display P3 + p3-d65-pq P3-D65 PQ + +You can also specify individual CICP values. For example, to label an RGB image decoded from a SECAM video: +Example usage: )" << program_name << R"( add --color_primaries 5 --transfer_function 4 --matrix_coefficients 0 --video_full_range_flag 1 )" << file_path << R"( + +These can be mixed to override defaults. Values specified later override prior values. +Example usage: )" << program_name << R"( add --preset display-p3 --video_full_range_flag 0 )" << file_path << R"( + +Actions: + -h --help Show help information (what you are viewing now) + -v --version Show version information + --license Show license information + add [flags] Adds a new cICP chunk to the PNG file + overwrite [flags] Adds a new or overwrites an existing cICP chunk + remove Removes the cICP chunk from the PNG file + +General flags: + -p --preset [value] Use [value]'s CICP values + -n --narrow Use narrow range (--video_full_range_flag 0) + -f --full Use full range (--video_full_range_flag 1) + +Specific flags to match CICP parameter names from ITU-T H.273 (experts only): + --color_primaries [value] + --transfer_function [value] + --matrix_coefficients [value] + --video_full_range_flag [value] +Note: Specific flags use values from ITU-T H.273. Not all values are valid. +PNG puts further restrictions on which values are valid. +)" << std::endl; + } + + void ActionExecutor::operator()(const LicenseAction&) const noexcept { + static const auto png_cicp_editor_year = std::string_view{"2025"}; + static const auto png_cicp_editor_copyright_holder = std::string_view{"The png_cicp_editor Contributors"}; + print_bsd_3_clause_license(png_cicp_editor_year, png_cicp_editor_copyright_holder); + + std::cout << R"( +png_cicp_editor depends on max: +)"; + + static const auto max_year = std::string_view{"2025"}; + static const auto max_copyright_holder = std::string_view{"The max Contributors"}; + print_bsd_3_clause_license(max_year, max_copyright_holder); + } + + + + void ActionExecutor::operator()(const AddAction& action) const noexcept { + // TODO: Return error values + // Read the file + auto file_contents = read_file(action.file_path_); + if (!file_contents.has_value()) { + print_error(file_contents.error()); + //return 1; + return; + } + + + // Get indicies of all chunks + auto chunk_indices = get_chunk_indices(file_contents.value()); + if (!chunk_indices.has_value()) { + print_error(chunk_indices.error()); + //return 1; + return; + } + + + // Find overwrite index for cICP chunk + auto split_buffer = get_split_buffer_across_cicp_insertion_point(file_contents.value(), chunk_indices.value(), /*overwrite_cicp*/ false); + if (!split_buffer.has_value()) { + print_error(split_buffer.error()); + //return 1; + return; + } + + + // Prepare cICP buffer to write + auto cicp_buffer = create_cicp_buffer(action.cicp_); + + + // Prepare file before & after buffers for write + std::vector> buffers; + buffers.push_back({ split_buffer.value()[0] }); + buffers.push_back({ cicp_buffer }); + buffers.push_back({ split_buffer.value()[1] }); + + + // Write the file with cICP inserted + auto write_result = PNG_CICP_Editor::write_file(action.file_path_, buffers); + if (!write_result.has_value()) { + print_error(write_result.error()); + //return 1; + return; + } + } + + void ActionExecutor::operator()(const OverwriteAction& action) const noexcept { + // TODO: Return error values + // Read the file + auto file_contents = read_file(action.file_path_); + if (!file_contents.has_value()) { + print_error(file_contents.error()); + //return 1; + return; + } + + + // Get indicies of all chunks + auto chunk_indices = get_chunk_indices(file_contents.value()); + if (!chunk_indices.has_value()) { + print_error(chunk_indices.error()); + //return 1; + return; + } + + + // Find overwrite index for cICP chunk + auto split_buffer = get_split_buffer_across_cicp_insertion_point(file_contents.value(), chunk_indices.value(), /*overwrite_cicp*/ true); + if (!split_buffer.has_value()) { + print_error(split_buffer.error()); + //return 1; + return; + } + + + // Prepare cICP buffer to write + auto cicp_buffer = create_cicp_buffer(action.cicp_); + + + // Prepare file before & after buffers for write + std::vector> buffers; + buffers.push_back({ split_buffer.value()[0] }); + buffers.push_back({ cicp_buffer }); + buffers.push_back({ split_buffer.value()[1] }); + + + // Write the file with cICP inserted + auto write_result = PNG_CICP_Editor::write_file(action.file_path_, buffers); + if (!write_result.has_value()) { + print_error(write_result.error()); + //return 1; + return; + } + } + + void ActionExecutor::operator()(const RemoveAction& action) const noexcept { + // TODO: Return error values + // Read the file + auto file_contents = read_file(action.file_path_); + if (!file_contents.has_value()) { + print_error(file_contents.error()); + //return 1; + return; + } + + + // Get indicies of all chunks + auto chunk_indices = get_chunk_indices(file_contents.value()); + if (!chunk_indices.has_value()) { + print_error(chunk_indices.error()); + //return 1; + return; + } + + + // TODO: Right now, this only removes the first cICP chunk found + // There might incorrectly be multiple. + auto cicp_index = size_t{ SIZE_MAX }; + auto after_cicp_index = size_t{ SIZE_MAX }; + auto chunk_count = chunk_indices->size(); + for (size_t i = 0; i < chunk_count; i++) { + auto index = (*chunk_indices)[i]; + if ((*file_contents)[index + 4] == 'c' && + (*file_contents)[index + 5] == 'I' && + (*file_contents)[index + 6] == 'C' && + (*file_contents)[index + 7] == 'P') { + cicp_index = index; + // the cICP chunk may incorrectly be the last chunk in the file, + // so there is no chunk after it + if (i < chunk_count - 1) { + after_cicp_index = (*chunk_indices)[i + 1]; + } + break; + } + } + + if (cicp_index == SIZE_MAX) { + // no cICP chunk found + print_error( Error{ { "no cICP chunk found" } } ); + return; + } + if (after_cicp_index == SIZE_MAX) { + // cICP was last chunk, which isn't allowed + print_error( Error{ { "Ill-formed file: cICP was the final chunk" } } ); + return; + } + + // Prepare file before & after buffers for write + auto file_contents_start = file_contents->data(); + std::vector> buffers; + buffers.push_back({ file_contents_start, file_contents_start + cicp_index }); + buffers.push_back({ file_contents_start + after_cicp_index, file_contents->size() - after_cicp_index }); + + + // Write the file with cICP inserted + auto write_result = PNG_CICP_Editor::write_file(action.file_path_, buffers); + if (!write_result.has_value()) { + print_error(write_result.error()); + //return 1; + return; + } + } + +} // namespace PNG_CICP_Editor diff --git a/Code/png_cicp_editor/Actions.hpp b/Code/png_cicp_editor/Actions.hpp index 0635347..e05ea64 100644 --- a/Code/png_cicp_editor/Actions.hpp +++ b/Code/png_cicp_editor/Actions.hpp @@ -1,93 +1,56 @@ -// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef PNG_CICP_EDITOR_ACTIONS_HPP -#define PNG_CICP_EDITOR_ACTIONS_HPP - -#include - -#include "CICP.hpp" - -namespace PNG_CICP_Editor { - - enum class Actions { - Version, - Help, - License, - Add, - Overwrite, - Remove, - }; - - struct VersionAction { - void operator()() const noexcept; - }; - - struct HelpAction { - void operator()() const noexcept; - }; - - struct LicenseAction { - void operator()() const noexcept; - }; - - struct AddAction { - explicit AddAction(CICP cicp, std::string file_path) noexcept; - - void operator()() const noexcept; - - CICP cicp_; - std::string file_path_; - }; - - struct OverwriteAction { - explicit OverwriteAction(CICP cicp, std::string file_path) noexcept; - - void operator()() const noexcept; - - CICP cicp_; - std::string file_path_; - }; - - struct RemoveAction { - explicit RemoveAction(std::string file_path) noexcept; - - void operator()() const noexcept; - - std::string file_path_; - }; - - struct Action { - - explicit Action(VersionAction version) noexcept; - explicit Action(HelpAction help) noexcept; - explicit Action(LicenseAction license) noexcept; - explicit Action(AddAction add) noexcept; - explicit Action(OverwriteAction overwrite) noexcept; - explicit Action(RemoveAction remove) noexcept; - - Action(const Action& rhs) noexcept; - Action(Action&& rhs) noexcept; - - ~Action() noexcept; - - Action& operator =(const Action& rhs) noexcept; - Action& operator =(Action&& rhs) noexcept; - - Actions action_type_; - union A { - VersionAction version_; - HelpAction help_; - LicenseAction license_; - AddAction add_; - OverwriteAction overwrite_; - RemoveAction remove_; - - ~A() noexcept; - } action_; - }; - -} // namespace PNG_CICP_Editor - -#endif // #ifndef PNG_CICP_EDITOR_ACTIONS_HPP +// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PNG_CICP_EDITOR_ACTIONS_HPP +#define PNG_CICP_EDITOR_ACTIONS_HPP + +#include +#include + +#include "CICP.hpp" + +namespace PNG_CICP_Editor { + + struct VersionAction {}; + + struct HelpAction {}; + + struct LicenseAction {}; + + struct AddAction { + explicit AddAction(CICP cicp, std::string file_path) noexcept; + + CICP cicp_; + std::string file_path_; + }; + + struct OverwriteAction { + explicit OverwriteAction(CICP cicp, std::string file_path) noexcept; + + CICP cicp_; + std::string file_path_; + }; + + struct RemoveAction { + explicit RemoveAction(std::string file_path) noexcept; + + std::string file_path_; + }; + + using Action = std::variant; + + struct ActionExecutor { + + void operator()(const PNG_CICP_Editor::VersionAction& action) const noexcept; + void operator()(const PNG_CICP_Editor::HelpAction& action) const noexcept; + void operator()(const PNG_CICP_Editor::LicenseAction& action) const noexcept; + void operator()(const PNG_CICP_Editor::AddAction& action) const noexcept; + void operator()(const PNG_CICP_Editor::OverwriteAction& action) const noexcept; + void operator()(const PNG_CICP_Editor::RemoveAction& action) const noexcept; + + }; + +} // namespace PNG_CICP_Editor + +#endif // #ifndef PNG_CICP_EDITOR_ACTIONS_HPP diff --git a/Code/png_cicp_editor/CICPCreator.cpp b/Code/png_cicp_editor/CICPCreator.cpp index 80c083d..7709b85 100644 --- a/Code/png_cicp_editor/CICPCreator.cpp +++ b/Code/png_cicp_editor/CICPCreator.cpp @@ -1,88 +1,108 @@ -// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "CICPCreator.hpp" - -namespace { - - // TODO: This implementation is ripped directly from the example CRC implementation in the spec: - // https://w3c.github.io/png/#samplecrc - // Surely, there is a better way for this use case. - unsigned long crc_table[256]; - int crc_table_computed = 0; - - void make_crc_table() noexcept { - unsigned long c; - int n, k; - - for (n = 0; n < 256; n++) { - c = (unsigned long)n; - for (k = 0; k < 8; k++) { - if (c & 1) { - c = 0xedb88320L ^ (c >> 1); - } - else { - c = c >> 1; - } - } - crc_table[n] = c; - } - crc_table_computed = 1; - } - - unsigned long update_crc(unsigned long crc, char* buf, int len) noexcept { - unsigned long c = crc; - int n; - - if (!crc_table_computed) { - make_crc_table(); - } - - for (n = 0; n < len; n++) { - c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8); - } - - return c; - } - - unsigned long crc(char* buf, int len) noexcept { - return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL; - } - -} // anonymous namespace - -namespace PNG_CICP_Editor { - - std::array create_cicp_buffer(const CICP& cicp) noexcept { - std::array buffer; - - // length - buffer[0] = 0; - buffer[1] = 0; - buffer[2] = 0; - buffer[3] = 4; - - // chunk type - buffer[4] = 'c'; - buffer[5] = 'I'; - buffer[6] = 'C'; - buffer[7] = 'P'; - - // chunk data - buffer[8] = cicp.color_primaries_; - buffer[9] = cicp.transfer_function_; - buffer[10] = cicp.matrix_coefficients_; - buffer[11] = cicp.video_full_range_flag_; - - // crc - unsigned long calculated_crc = crc(&buffer[4], 8); - buffer[12] = static_cast(calculated_crc >> 24); - buffer[13] = static_cast(calculated_crc >> 16); - buffer[14] = static_cast(calculated_crc >> 8); - buffer[15] = static_cast(calculated_crc >> 0); - - return buffer; - } - -} // namespace PNG_CICP_Editor +// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "CICPCreator.hpp" + +namespace { + + // TODO: This implementation is ripped directly from the example CRC implementation in the spec: + // https://w3c.github.io/png/#samplecrc + // Surely, there is a better way for this use case. + unsigned long crc_table[256]; + int crc_table_computed = 0; + + void make_crc_table() noexcept { + unsigned long c; + int n, k; + + for (n = 0; n < 256; n++) { + c = (unsigned long)n; + for (k = 0; k < 8; k++) { + if (c & 1) { + c = 0xedb88320L ^ (c >> 1); + } + else { + c = c >> 1; + } + } + crc_table[n] = c; + } + crc_table_computed = 1; + } + + unsigned long update_crc(unsigned long crc, char* buf, int len) noexcept { + unsigned long c = crc; + int n; + + if (!crc_table_computed) { + make_crc_table(); + } + + for (n = 0; n < len; n++) { + c = crc_table[(c ^ buf[n]) & 0xff] ^ (c >> 8); + } + + return c; + } + + unsigned long crc(char* buf, int len) noexcept { + return update_crc(0xffffffffL, buf, len) ^ 0xffffffffL; + } + + // https://www.youtube.com/watch?v=m7PVZixO35c + unsigned computer_crc32(unsigned char* data, int length, int* zerop) noexcept { + uint32_t crc = 0xffffffff; + const uint32_t polynomial = 0xedb88320; + + int zeroes = 0; + int i, j; + for (i = 0; i < length; i++) { + crc ^= data[i]; + for (j = 0; j < 8; j++) { + // Do it without branching, use boolean operations to xor with the + // polynomial or zero depending on whether last bit is set + //zeroes += (1-(crc & 1)); + crc = (crc >> 1) ^ ((0-(crc & 1)) & polynomial); + } + } + *zerop = zeroes; + return crc; + } + +} // anonymous namespace + +namespace PNG_CICP_Editor { + + std::array create_cicp_buffer(const CICP& cicp) noexcept { + std::array buffer; + + // length + buffer[0] = 0; + buffer[1] = 0; + buffer[2] = 0; + buffer[3] = 4; + + // chunk type + buffer[4] = 'c'; + buffer[5] = 'I'; + buffer[6] = 'C'; + buffer[7] = 'P'; + + // chunk data + buffer[8] = cicp.color_primaries_; + buffer[9] = cicp.transfer_function_; + buffer[10] = cicp.matrix_coefficients_; + buffer[11] = cicp.video_full_range_flag_; + + // crc + unsigned long calculated_crc = crc(&buffer[4], 8); + buffer[12] = static_cast(calculated_crc >> 24); + buffer[13] = static_cast(calculated_crc >> 16); + buffer[14] = static_cast(calculated_crc >> 8); + buffer[15] = static_cast(calculated_crc >> 0); + + return buffer; + } + +} // namespace PNG_CICP_Editor diff --git a/Code/png_cicp_editor/CommandLineParameters.cpp b/Code/png_cicp_editor/CommandLineParameters.cpp index 9fb8f33..6fa199d 100644 --- a/Code/png_cicp_editor/CommandLineParameters.cpp +++ b/Code/png_cicp_editor/CommandLineParameters.cpp @@ -1,567 +1,575 @@ -// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "CommandLineParameters.hpp" - -#include -#include -#include -#include -#include -#include - -#include "StateMachine.hpp" -#include "SparseArray.hpp" - -#include - -namespace { - - // error messages - static constinit std::string_view unrecognized_parameter = "Unrecognized command line parameter: "; - static constinit std::string_view value_outside_range = "Value outside 0-255 range: "; - static constinit std::string_view expected_value = "File path provided where a value was expected: "; - - // utility - static constinit std::string_view newline = "\n"; - - enum class ReadNumericValueErrorCode { - UnrecognizedParameter, - ValueOutsideRange, - }; - using ReadNumericValueError = PNG_CICP_Editor::ErrorWithCode; - - std::expected read_numeric_value(char const* parameter) noexcept { - static constinit int base = 10; - char* end_pointer = nullptr; - long value = std::strtol(parameter, &end_pointer, base); - if (errno == ERANGE || parameter == end_pointer) { - return std::unexpected{ ReadNumericValueError{ ReadNumericValueErrorCode::UnrecognizedParameter, { unrecognized_parameter, parameter, newline } } }; - } - - if (value < 0 || value > 255) { - return std::unexpected{ ReadNumericValueError{ ReadNumericValueErrorCode::ValueOutsideRange, { value_outside_range, parameter, newline } } }; - } - - return static_cast(value); - } - - PNG_CICP_Editor::ParseCommandLineParametersError convert_error(ReadNumericValueError error) noexcept { - PNG_CICP_Editor::ParseCommandLineParametersErrorCode new_error_code; - switch (error.error_code_) { - case ReadNumericValueErrorCode::UnrecognizedParameter: - new_error_code = PNG_CICP_Editor::ParseCommandLineParametersErrorCode::UnrecognizedParameter; - break; - case ReadNumericValueErrorCode::ValueOutsideRange: - new_error_code = PNG_CICP_Editor::ParseCommandLineParametersErrorCode::ValueOutsideRange; - break; - default: - std::unreachable(); - } - - return PNG_CICP_Editor::ParseCommandLineParametersError{ std::move(new_error_code), std::move(error.output_messages_) }; - } - -} // anonymous namespace - -namespace PNG_CICP_Editor { - - std::expected parse_command_line_parameters(int argc, char const* argv[]) noexcept { - enum class ParserStates { - ExpectingAction, - ExpectingFlag, - ExpectingPresetValue, - ExpectingColorPrimariesValue, - ExpectingTransferFunctionValue, - ExpectingMatrixCoefficientsValue, - ExpectingVideoFullRangeFlagValue, - ExpectingFlagOrFile, - ExpectingFile, - Done, - }; - class Transition { - public: - - using PredicateAndActionResultType = std::expected; - //using PredicateAndActionType = std::move_only_function; - using PredicateAndActionType = std::function; - - explicit Transition(PredicateAndActionType predicate_and_action, ParserStates transition_to) noexcept - : predicate_and_action_(std::move(predicate_and_action)) - , transition_to_(std::move(transition_to)) - {} - - PredicateAndActionType predicate_and_action_; - ParserStates transition_to_; - - }; - - auto action = Actions::Help; - auto cicp = CICP{ /*color_primaires=*/0, /*transfer_function=*/0, /*matrix_coefficients=*/0, /*video_full_range_flag=*/1 }; - char const* file_path = ""; - - - auto version_action_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view version_string = "--version"; - static const std::string_view v_string = "-v"; - if (version_string.compare(string) != 0 && v_string.compare(string) != 0) { - return false; - } - action = Actions::Version; - return true; - }, - ParserStates::Done - }; - auto help_action_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view help_string = "--help"; - static const std::string_view h_string = "-h"; - if (help_string.compare(string) != 0 && h_string.compare(string) != 0) { - return false; - } - action = Actions::Help; - return true; - }, - ParserStates::Done - }; - auto license_action_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view license_string = "--license"; - if (license_string.compare(string) != 0) { - return false; - } - action = Actions::License; - return true; - }, - ParserStates::Done - }; - auto add_action_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view add_string = "add"; - if (add_string.compare(string) != 0) { - return false; - } - action = Actions::Add; - return true; - }, - ParserStates::ExpectingFlag - }; - auto overwrite_action_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view overwrite_string = "overwrite"; - if (overwrite_string.compare(string) != 0) { - return false; - } - action = Actions::Overwrite; - return true; - }, - ParserStates::ExpectingFlag - }; - auto remove_action_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view remove_string = "remove"; - if (remove_string.compare(string) != 0) { - return false; - } - action = Actions::Remove; - return true; - }, - ParserStates::ExpectingFile - }; - auto preset_flag_matched_transition = Transition{ - [](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view preset_string = "--preset"; - static const std::string_view p_string = "-p"; - return (preset_string.compare(string) == 0) || (p_string.compare(string) == 0); - }, - ParserStates::ExpectingPresetValue - }; - auto color_primaries_flag_matched_transition = Transition{ - [](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view color_primaries_string = "--color_primaries"; - return color_primaries_string.compare(string) == 0; - }, - ParserStates::ExpectingColorPrimariesValue - }; - auto transfer_function_flag_matched_transition = Transition{ - [](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view transfer_function_string = "--transfer_function"; - return transfer_function_string.compare(string) == 0; - }, - ParserStates::ExpectingTransferFunctionValue - }; - auto matrix_coefficients_flag_matched_transition = Transition{ - [](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view matrix_coefficients_string = "--matrix_coefficients"; - return matrix_coefficients_string.compare(string) == 0; - }, - ParserStates::ExpectingMatrixCoefficientsValue - }; - auto video_full_range_flag_flag_matched_transition = Transition{ - [](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view video_full_range_flag_string = "--video_full_range_flag"; - return video_full_range_flag_string.compare(string) == 0; - }, - ParserStates::ExpectingVideoFullRangeFlagValue - }; - auto narrow_flag_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view narrow_string = "--narrow"; - static const std::string_view n_string = "-n"; - if (narrow_string.compare(string) != 0 && n_string.compare(string) != 0) { - return false; - } - cicp.video_full_range_flag_ = 0; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - auto full_flag_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view full_string = "--full"; - static const std::string_view f_string = "-f"; - if (full_string.compare(string) != 0 && f_string.compare(string) != 0) { - return false; - } - cicp.video_full_range_flag_ = 1; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - auto file_found_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - file_path = string; - return true; - }, - ParserStates::Done - }; - auto bt601_pal_value_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view bt601_pal_string = "bt.601-pal"; - if (bt601_pal_string.compare(string) != 0) { - return false; - } - cicp.color_primaries_ = 5; - cicp.transfer_function_ = 6; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - auto bt601_ntsc_value_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view bt601_ntsc_string = "bt.601-ntsc"; - if (bt601_ntsc_string.compare(string) != 0) { - return false; - } - cicp.color_primaries_ = 6; - cicp.transfer_function_ = 6; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - auto bt709_value_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view bt709_string = "bt.709"; - if (bt709_string.compare(string) != 0) { - return false; - } - cicp.color_primaries_ = 1; - cicp.transfer_function_ = 1; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - auto linear_light_srgb_value_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view linear_light_srgb_string = "linear-light-srgb"; - if (linear_light_srgb_string.compare(string) != 0) { - return false; - } - cicp.color_primaries_ = 1; - cicp.transfer_function_ = 8; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - auto srgb_value_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view srgb_string = "srgb"; - if (srgb_string.compare(string) != 0) { - return false; - } - cicp.color_primaries_ = 1; - cicp.transfer_function_ = 13; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - auto bt2020_10bit_value_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view bt2020_10bit_string = "bt.2020-10-bit"; - if (bt2020_10bit_string.compare(string) != 0) { - return false; - } - cicp.color_primaries_ = 9; - cicp.transfer_function_ = 14; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - auto bt2020_12bit_value_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view bt2020_12bit_string = "bt.2020-12-bit"; - if (bt2020_12bit_string.compare(string) != 0) { - return false; - } - cicp.color_primaries_ = 9; - cicp.transfer_function_ = 15; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - auto bt2100_pq_value_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view bt2100_pq_string = "bt.2100-pq"; - if (bt2100_pq_string.compare(string) != 0) { - return false; - } - cicp.color_primaries_ = 9; - cicp.transfer_function_ = 16; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - auto bt2100_hlg_value_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view bt2100_hlg_string = "bt.2100-hlg"; - if (bt2100_hlg_string.compare(string) != 0) { - return false; - } - cicp.color_primaries_ = 9; - cicp.transfer_function_ = 18; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - auto dci_p3_value_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view dci_p3_string = "dci-p3"; - if (dci_p3_string.compare(string) != 0) { - return false; - } - cicp.color_primaries_ = 11; - cicp.transfer_function_ = 17; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - auto display_p3_value_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view display_p3_string = "display-p3"; - if (display_p3_string.compare(string) != 0) { - return false; - } - cicp.color_primaries_ = 12; - cicp.transfer_function_ = 13; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - auto p3_d65_pq_value_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - static const std::string_view p3_d65_pq_string = "p3-d65-pq"; - if (p3_d65_pq_string.compare(string) != 0) { - return false; - } - cicp.color_primaries_ = 12; - cicp.transfer_function_ = 16; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - auto color_primaries_value_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - auto result = read_numeric_value(string); - - if (!result.has_value()) { - switch (result.error().error_code_) { - case ReadNumericValueErrorCode::UnrecognizedParameter: - return false; - case ReadNumericValueErrorCode::ValueOutsideRange: - return std::unexpected{ TransitionError{ TransitionErrorCode::ValueOutsideRange, std::move(result.error().output_messages_) } }; - } - return false; - } - - cicp.color_primaries_ = *result; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - auto transfer_function_value_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - auto result = read_numeric_value(string); - - if (!result.has_value()) { - switch (result.error().error_code_) { - case ReadNumericValueErrorCode::UnrecognizedParameter: - return false; - case ReadNumericValueErrorCode::ValueOutsideRange: - return std::unexpected{ TransitionError{ TransitionErrorCode::ValueOutsideRange, std::move(result.error().output_messages_) } }; - } - return false; - } - - cicp.transfer_function_ = *result; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - auto matrix_coefficients_value_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - auto result = read_numeric_value(string); - - if (!result.has_value()) { - switch (result.error().error_code_) { - case ReadNumericValueErrorCode::UnrecognizedParameter: - return false; - case ReadNumericValueErrorCode::ValueOutsideRange: - return std::unexpected{ TransitionError{ TransitionErrorCode::ValueOutsideRange, std::move(result.error().output_messages_) } }; - } - return false; - } - - cicp.matrix_coefficients_ = *result; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - auto video_full_range_flag_value_matched_transition = Transition{ - [&](char const* string) -> Transition::PredicateAndActionResultType { - auto result = read_numeric_value(string); - - if (!result.has_value()) { - switch (result.error().error_code_) { - case ReadNumericValueErrorCode::UnrecognizedParameter: - return false; - case ReadNumericValueErrorCode::ValueOutsideRange: - return std::unexpected{ TransitionError{ TransitionErrorCode::ValueOutsideRange, std::move(result.error().output_messages_) } }; - } - return false; - } - - cicp.video_full_range_flag_ = *result; - return true; - }, - ParserStates::ExpectingFlagOrFile - }; - - - using TransitionsType = std::vector; - auto action_found_transitions = TransitionsType{ - std::move(version_action_matched_transition), - std::move(help_action_matched_transition), - std::move(license_action_matched_transition), - std::move(add_action_matched_transition), - std::move(overwrite_action_matched_transition), - std::move(remove_action_matched_transition), - }; - auto flag_found_transitions = TransitionsType{ - // Note: These are not moved because they are used below - preset_flag_matched_transition, - narrow_flag_matched_transition, - full_flag_matched_transition, - color_primaries_flag_matched_transition, - transfer_function_flag_matched_transition, - matrix_coefficients_flag_matched_transition, - video_full_range_flag_flag_matched_transition, - }; - auto flag_or_file_found_transitions = TransitionsType{ - std::move(preset_flag_matched_transition), - std::move(narrow_flag_matched_transition), - std::move(full_flag_matched_transition), - std::move(color_primaries_flag_matched_transition), - std::move(transfer_function_flag_matched_transition), - std::move(matrix_coefficients_flag_matched_transition), - std::move(video_full_range_flag_flag_matched_transition), - // Note: These are not moved because they are used below - file_found_transition, - }; - auto preset_value_found_transitions = TransitionsType{ - std::move(bt601_pal_value_matched_transition), - std::move(bt601_ntsc_value_matched_transition), - std::move(bt709_value_matched_transition), - std::move(linear_light_srgb_value_matched_transition), - std::move(srgb_value_matched_transition), - std::move(bt2020_10bit_value_matched_transition), - std::move(bt2020_12bit_value_matched_transition), - std::move(bt2100_pq_value_matched_transition), - std::move(bt2100_hlg_value_matched_transition), - std::move(dci_p3_value_matched_transition), - std::move(display_p3_value_matched_transition), - std::move(p3_d65_pq_value_matched_transition), - }; - auto color_primaries_found_transitions = TransitionsType{ std::move(color_primaries_value_matched_transition) }; - auto transfer_function_found_transitions = TransitionsType{ std::move(transfer_function_value_matched_transition) }; - auto matrix_coefficients_found_transitions = TransitionsType{ std::move(matrix_coefficients_value_matched_transition) }; - auto video_full_range_flag_found_transitions = TransitionsType{ std::move(video_full_range_flag_value_matched_transition) }; - auto file_found_transitions = TransitionsType{ std::move(file_found_transition) }; - - auto parser_state_machine = StateMachine{ ParserStates::ExpectingAction, SparseArray{ { - { ParserStates::ExpectingAction, action_found_transitions }, - { ParserStates::ExpectingFlag, flag_found_transitions }, - { ParserStates::ExpectingFlagOrFile, flag_or_file_found_transitions }, - { ParserStates::ExpectingPresetValue, preset_value_found_transitions }, - { ParserStates::ExpectingColorPrimariesValue, color_primaries_found_transitions }, - { ParserStates::ExpectingTransferFunctionValue, transfer_function_found_transitions }, - { ParserStates::ExpectingMatrixCoefficientsValue, matrix_coefficients_found_transitions }, - { ParserStates::ExpectingVideoFullRangeFlagValue, video_full_range_flag_found_transitions }, - { ParserStates::ExpectingFile, file_found_transitions }, - } } }; - - // Start at 1 because the first command line parameter is the program name. - // Loop until argc - 1 because the final parameter should be the file path. - if (argc == 1) { - // TODO: Handle this - } - for (int i = 1; i < argc; i++) { - auto transition_result = parser_state_machine.Transition(argv[i]); - if (!transition_result.has_value()) { - switch (transition_result.error().error_code_) { - case TransitionErrorCode::StateNotInStateMachine: - return std::unexpected{ ParseCommandLineParametersError{ ParseCommandLineParametersErrorCode::UnrecognizedParameter, { unrecognized_parameter, argv[i], newline } } }; - case TransitionErrorCode::NoTransitionMatchedPredicate: - return std::unexpected{ ParseCommandLineParametersError{ ParseCommandLineParametersErrorCode::UnrecognizedParameter, { expected_value, argv[i], newline } } }; - case TransitionErrorCode::ValueOutsideRange: - return std::unexpected{ ParseCommandLineParametersError{ ParseCommandLineParametersErrorCode::ValueOutsideRange, std::move(transition_result.error().output_messages_) } }; - } - } - } - if (parser_state_machine.state_ != ParserStates::Done) { - return std::unexpected{ ParseCommandLineParametersError{ ParseCommandLineParametersErrorCode::ExpectedValue, { expected_value, argv[argc - 1], newline } } }; - } - - - // TODO: Test if a file path with a space and quotes ("C:\test images\test.png") is provided as one command line parameter - - switch (action) { - case Actions::Version: - return Action{ VersionAction{} }; - case Actions::Help: - return Action{ HelpAction{} }; - case Actions::License: - return Action{ LicenseAction{} }; - case Actions::Add: - return Action{ AddAction{ std::move(cicp), std::move(file_path) } }; - case Actions::Overwrite: - return Action{ OverwriteAction{ std::move(cicp), std::move(file_path) } }; - case Actions::Remove: - return Action{ RemoveAction{ std::move(file_path) } }; - } - MAX_UNREACHABLE; - - } - -} // namespace PNG_CICP_Editor +// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "CommandLineParameters.hpp" + +#include +#include +#include +#include +#include + +#include "StateMachine.hpp" +#include "SparseArray.hpp" + +#include + +namespace { + + enum class Actions { + Version, + Help, + License, + Add, + Overwrite, + Remove, + }; + + // error messages + static constinit std::string_view unrecognized_parameter = "Unrecognized command line parameter: "; + static constinit std::string_view value_outside_range = "Value outside 0-255 range: "; + static constinit std::string_view expected_value = "File path provided where a value was expected: "; + + // utility + static constinit std::string_view newline = "\n"; + + enum class ReadNumericValueErrorCode { + UnrecognizedParameter, + ValueOutsideRange, + }; + using ReadNumericValueError = PNG_CICP_Editor::ErrorWithCode; + + std::expected read_numeric_value(char const* parameter) noexcept { + static constinit int base = 10; + char* end_pointer = nullptr; + long value = std::strtol(parameter, &end_pointer, base); + if (errno == ERANGE || parameter == end_pointer) { + return std::unexpected{ ReadNumericValueError{ ReadNumericValueErrorCode::UnrecognizedParameter, { unrecognized_parameter, parameter, newline } } }; + } + + if (value < 0 || value > 255) { + return std::unexpected{ ReadNumericValueError{ ReadNumericValueErrorCode::ValueOutsideRange, { value_outside_range, parameter, newline } } }; + } + + return static_cast(value); + } + + PNG_CICP_Editor::ParseCommandLineParametersError convert_error(ReadNumericValueError error) noexcept { + PNG_CICP_Editor::ParseCommandLineParametersErrorCode new_error_code; + switch (error.error_code_) { + case ReadNumericValueErrorCode::UnrecognizedParameter: + new_error_code = PNG_CICP_Editor::ParseCommandLineParametersErrorCode::UnrecognizedParameter; + break; + case ReadNumericValueErrorCode::ValueOutsideRange: + new_error_code = PNG_CICP_Editor::ParseCommandLineParametersErrorCode::ValueOutsideRange; + break; + default: + std::unreachable(); + } + + return PNG_CICP_Editor::ParseCommandLineParametersError{ std::move(new_error_code), std::move(error.output_messages_) }; + } + +} // anonymous namespace + +namespace PNG_CICP_Editor { + + std::expected parse_command_line_parameters(int argc, char const* argv[]) noexcept { + enum class ParserStates { + ExpectingAction, + ExpectingFlag, + ExpectingPresetValue, + ExpectingColorPrimariesValue, + ExpectingTransferFunctionValue, + ExpectingMatrixCoefficientsValue, + ExpectingVideoFullRangeFlagValue, + ExpectingFlagOrFile, + ExpectingFile, + Done, + }; + class Transition { + public: + + using PredicateAndActionResultType = std::expected; + //using PredicateAndActionType = std::move_only_function; + using PredicateAndActionType = std::function; + + explicit Transition(PredicateAndActionType predicate_and_action, ParserStates transition_to) noexcept + : predicate_and_action_(std::move(predicate_and_action)) + , transition_to_(std::move(transition_to)) + {} + + PredicateAndActionType predicate_and_action_; + ParserStates transition_to_; + + }; + + auto action = Actions::Help; + auto cicp = CICP{ /*color_primaires=*/0, /*transfer_function=*/0, /*matrix_coefficients=*/0, /*video_full_range_flag=*/1 }; + char const* file_path = ""; + + + auto version_action_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view version_string = "--version"; + static const std::string_view v_string = "-v"; + if (version_string.compare(string) != 0 && v_string.compare(string) != 0) { + return false; + } + action = Actions::Version; + return true; + }, + ParserStates::Done + }; + auto help_action_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view help_string = "--help"; + static const std::string_view h_string = "-h"; + if (help_string.compare(string) != 0 && h_string.compare(string) != 0) { + return false; + } + action = Actions::Help; + return true; + }, + ParserStates::Done + }; + auto license_action_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view license_string = "--license"; + if (license_string.compare(string) != 0) { + return false; + } + action = Actions::License; + return true; + }, + ParserStates::Done + }; + auto add_action_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view add_string = "add"; + if (add_string.compare(string) != 0) { + return false; + } + action = Actions::Add; + return true; + }, + ParserStates::ExpectingFlag + }; + auto overwrite_action_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view overwrite_string = "overwrite"; + if (overwrite_string.compare(string) != 0) { + return false; + } + action = Actions::Overwrite; + return true; + }, + ParserStates::ExpectingFlag + }; + auto remove_action_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view remove_string = "remove"; + if (remove_string.compare(string) != 0) { + return false; + } + action = Actions::Remove; + return true; + }, + ParserStates::ExpectingFile + }; + auto preset_flag_matched_transition = Transition{ + [](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view preset_string = "--preset"; + static const std::string_view p_string = "-p"; + return (preset_string.compare(string) == 0) || (p_string.compare(string) == 0); + }, + ParserStates::ExpectingPresetValue + }; + auto color_primaries_flag_matched_transition = Transition{ + [](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view color_primaries_string = "--color_primaries"; + return color_primaries_string.compare(string) == 0; + }, + ParserStates::ExpectingColorPrimariesValue + }; + auto transfer_function_flag_matched_transition = Transition{ + [](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view transfer_function_string = "--transfer_function"; + return transfer_function_string.compare(string) == 0; + }, + ParserStates::ExpectingTransferFunctionValue + }; + auto matrix_coefficients_flag_matched_transition = Transition{ + [](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view matrix_coefficients_string = "--matrix_coefficients"; + return matrix_coefficients_string.compare(string) == 0; + }, + ParserStates::ExpectingMatrixCoefficientsValue + }; + auto video_full_range_flag_flag_matched_transition = Transition{ + [](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view video_full_range_flag_string = "--video_full_range_flag"; + return video_full_range_flag_string.compare(string) == 0; + }, + ParserStates::ExpectingVideoFullRangeFlagValue + }; + auto narrow_flag_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view narrow_string = "--narrow"; + static const std::string_view n_string = "-n"; + if (narrow_string.compare(string) != 0 && n_string.compare(string) != 0) { + return false; + } + cicp.video_full_range_flag_ = 0; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + auto full_flag_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view full_string = "--full"; + static const std::string_view f_string = "-f"; + if (full_string.compare(string) != 0 && f_string.compare(string) != 0) { + return false; + } + cicp.video_full_range_flag_ = 1; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + auto file_found_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + file_path = string; + return true; + }, + ParserStates::Done + }; + auto bt601_pal_value_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view bt601_pal_string = "bt.601-pal"; + if (bt601_pal_string.compare(string) != 0) { + return false; + } + cicp.color_primaries_ = 5; + cicp.transfer_function_ = 6; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + auto bt601_ntsc_value_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view bt601_ntsc_string = "bt.601-ntsc"; + if (bt601_ntsc_string.compare(string) != 0) { + return false; + } + cicp.color_primaries_ = 6; + cicp.transfer_function_ = 6; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + auto bt709_value_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view bt709_string = "bt.709"; + if (bt709_string.compare(string) != 0) { + return false; + } + cicp.color_primaries_ = 1; + cicp.transfer_function_ = 1; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + auto linear_light_srgb_value_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view linear_light_srgb_string = "linear-light-srgb"; + if (linear_light_srgb_string.compare(string) != 0) { + return false; + } + cicp.color_primaries_ = 1; + cicp.transfer_function_ = 8; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + auto srgb_value_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view srgb_string = "srgb"; + if (srgb_string.compare(string) != 0) { + return false; + } + cicp.color_primaries_ = 1; + cicp.transfer_function_ = 13; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + auto bt2020_10bit_value_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view bt2020_10bit_string = "bt.2020-10-bit"; + if (bt2020_10bit_string.compare(string) != 0) { + return false; + } + cicp.color_primaries_ = 9; + cicp.transfer_function_ = 14; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + auto bt2020_12bit_value_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view bt2020_12bit_string = "bt.2020-12-bit"; + if (bt2020_12bit_string.compare(string) != 0) { + return false; + } + cicp.color_primaries_ = 9; + cicp.transfer_function_ = 15; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + auto bt2100_pq_value_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view bt2100_pq_string = "bt.2100-pq"; + if (bt2100_pq_string.compare(string) != 0) { + return false; + } + cicp.color_primaries_ = 9; + cicp.transfer_function_ = 16; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + auto bt2100_hlg_value_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view bt2100_hlg_string = "bt.2100-hlg"; + if (bt2100_hlg_string.compare(string) != 0) { + return false; + } + cicp.color_primaries_ = 9; + cicp.transfer_function_ = 18; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + auto dci_p3_value_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view dci_p3_string = "dci-p3"; + if (dci_p3_string.compare(string) != 0) { + return false; + } + cicp.color_primaries_ = 11; + cicp.transfer_function_ = 17; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + auto display_p3_value_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view display_p3_string = "display-p3"; + if (display_p3_string.compare(string) != 0) { + return false; + } + cicp.color_primaries_ = 12; + cicp.transfer_function_ = 13; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + auto p3_d65_pq_value_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + static const std::string_view p3_d65_pq_string = "p3-d65-pq"; + if (p3_d65_pq_string.compare(string) != 0) { + return false; + } + cicp.color_primaries_ = 12; + cicp.transfer_function_ = 16; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + auto color_primaries_value_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + auto result = read_numeric_value(string); + + if (!result.has_value()) { + switch (result.error().error_code_) { + case ReadNumericValueErrorCode::UnrecognizedParameter: + return false; + case ReadNumericValueErrorCode::ValueOutsideRange: + return std::unexpected{ TransitionError{ TransitionErrorCode::ValueOutsideRange, std::move(result.error().output_messages_) } }; + } + return false; + } + + cicp.color_primaries_ = *result; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + auto transfer_function_value_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + auto result = read_numeric_value(string); + + if (!result.has_value()) { + switch (result.error().error_code_) { + case ReadNumericValueErrorCode::UnrecognizedParameter: + return false; + case ReadNumericValueErrorCode::ValueOutsideRange: + return std::unexpected{ TransitionError{ TransitionErrorCode::ValueOutsideRange, std::move(result.error().output_messages_) } }; + } + return false; + } + + cicp.transfer_function_ = *result; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + auto matrix_coefficients_value_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + auto result = read_numeric_value(string); + + if (!result.has_value()) { + switch (result.error().error_code_) { + case ReadNumericValueErrorCode::UnrecognizedParameter: + return false; + case ReadNumericValueErrorCode::ValueOutsideRange: + return std::unexpected{ TransitionError{ TransitionErrorCode::ValueOutsideRange, std::move(result.error().output_messages_) } }; + } + return false; + } + + cicp.matrix_coefficients_ = *result; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + auto video_full_range_flag_value_matched_transition = Transition{ + [&](char const* string) -> Transition::PredicateAndActionResultType { + auto result = read_numeric_value(string); + + if (!result.has_value()) { + switch (result.error().error_code_) { + case ReadNumericValueErrorCode::UnrecognizedParameter: + return false; + case ReadNumericValueErrorCode::ValueOutsideRange: + return std::unexpected{ TransitionError{ TransitionErrorCode::ValueOutsideRange, std::move(result.error().output_messages_) } }; + } + return false; + } + + cicp.video_full_range_flag_ = *result; + return true; + }, + ParserStates::ExpectingFlagOrFile + }; + + + using TransitionsType = std::vector; + auto action_found_transitions = TransitionsType{ + std::move(version_action_matched_transition), + std::move(help_action_matched_transition), + std::move(license_action_matched_transition), + std::move(add_action_matched_transition), + std::move(overwrite_action_matched_transition), + std::move(remove_action_matched_transition), + }; + auto flag_found_transitions = TransitionsType{ + // Note: These are not moved because they are used below + preset_flag_matched_transition, + narrow_flag_matched_transition, + full_flag_matched_transition, + color_primaries_flag_matched_transition, + transfer_function_flag_matched_transition, + matrix_coefficients_flag_matched_transition, + video_full_range_flag_flag_matched_transition, + }; + auto flag_or_file_found_transitions = TransitionsType{ + std::move(preset_flag_matched_transition), + std::move(narrow_flag_matched_transition), + std::move(full_flag_matched_transition), + std::move(color_primaries_flag_matched_transition), + std::move(transfer_function_flag_matched_transition), + std::move(matrix_coefficients_flag_matched_transition), + std::move(video_full_range_flag_flag_matched_transition), + // Note: These are not moved because they are used below + file_found_transition, + }; + auto preset_value_found_transitions = TransitionsType{ + std::move(bt601_pal_value_matched_transition), + std::move(bt601_ntsc_value_matched_transition), + std::move(bt709_value_matched_transition), + std::move(linear_light_srgb_value_matched_transition), + std::move(srgb_value_matched_transition), + std::move(bt2020_10bit_value_matched_transition), + std::move(bt2020_12bit_value_matched_transition), + std::move(bt2100_pq_value_matched_transition), + std::move(bt2100_hlg_value_matched_transition), + std::move(dci_p3_value_matched_transition), + std::move(display_p3_value_matched_transition), + std::move(p3_d65_pq_value_matched_transition), + }; + auto color_primaries_found_transitions = TransitionsType{ std::move(color_primaries_value_matched_transition) }; + auto transfer_function_found_transitions = TransitionsType{ std::move(transfer_function_value_matched_transition) }; + auto matrix_coefficients_found_transitions = TransitionsType{ std::move(matrix_coefficients_value_matched_transition) }; + auto video_full_range_flag_found_transitions = TransitionsType{ std::move(video_full_range_flag_value_matched_transition) }; + auto file_found_transitions = TransitionsType{ std::move(file_found_transition) }; + + auto parser_state_machine = StateMachine{ ParserStates::ExpectingAction, SparseArray{ { + { ParserStates::ExpectingAction, action_found_transitions }, + { ParserStates::ExpectingFlag, flag_found_transitions }, + { ParserStates::ExpectingFlagOrFile, flag_or_file_found_transitions }, + { ParserStates::ExpectingPresetValue, preset_value_found_transitions }, + { ParserStates::ExpectingColorPrimariesValue, color_primaries_found_transitions }, + { ParserStates::ExpectingTransferFunctionValue, transfer_function_found_transitions }, + { ParserStates::ExpectingMatrixCoefficientsValue, matrix_coefficients_found_transitions }, + { ParserStates::ExpectingVideoFullRangeFlagValue, video_full_range_flag_found_transitions }, + { ParserStates::ExpectingFile, file_found_transitions }, + } } }; + + // Start at 1 because the first command line parameter is the program name. + // Loop until argc - 1 because the final parameter should be the file path. + if (argc == 1) { + // TODO: Handle this + } + for (int i = 1; i < argc; i++) { + auto transition_result = parser_state_machine.Transition(argv[i]); + if (!transition_result.has_value()) { + switch (transition_result.error().error_code_) { + case TransitionErrorCode::StateNotInStateMachine: + return std::unexpected{ ParseCommandLineParametersError{ ParseCommandLineParametersErrorCode::UnrecognizedParameter, { unrecognized_parameter, argv[i], newline } } }; + case TransitionErrorCode::NoTransitionMatchedPredicate: + return std::unexpected{ ParseCommandLineParametersError{ ParseCommandLineParametersErrorCode::UnrecognizedParameter, { expected_value, argv[i], newline } } }; + case TransitionErrorCode::ValueOutsideRange: + return std::unexpected{ ParseCommandLineParametersError{ ParseCommandLineParametersErrorCode::ValueOutsideRange, std::move(transition_result.error().output_messages_) } }; + } + } + } + if (parser_state_machine.state_ != ParserStates::Done) { + return std::unexpected{ ParseCommandLineParametersError{ ParseCommandLineParametersErrorCode::ExpectedValue, { expected_value, argv[argc - 1], newline } } }; + } + + + // TODO: Test if a file path with a space and quotes ("C:\test images\test.png") is provided as one command line parameter + + switch (action) { + case Actions::Version: + return Action{ VersionAction{} }; + case Actions::Help: + return Action{ HelpAction{} }; + case Actions::License: + return Action{ LicenseAction{} }; + case Actions::Add: + return Action{ AddAction{ std::move(cicp), std::move(file_path) } }; + case Actions::Overwrite: + return Action{ OverwriteAction{ std::move(cicp), std::move(file_path) } }; + case Actions::Remove: + return Action{ RemoveAction{ std::move(file_path) } }; + } + MAX_UNREACHABLE; + + } + +} // namespace PNG_CICP_Editor diff --git a/Code/png_cicp_editor/CommandLineParameters.hpp b/Code/png_cicp_editor/CommandLineParameters.hpp index b0a4b77..5c1ca64 100644 --- a/Code/png_cicp_editor/CommandLineParameters.hpp +++ b/Code/png_cicp_editor/CommandLineParameters.hpp @@ -1,26 +1,40 @@ -// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef PNG_CICP_EDITOR_COMMANDLINEPARAMETERS_HPP -#define PNG_CICP_EDITOR_COMMANDLINEPARAMETERS_HPP - -#include - -#include "Actions.hpp" -#include "Error.hpp" - -namespace PNG_CICP_Editor { - - enum class ParseCommandLineParametersErrorCode { - UnrecognizedParameter, - ValueOutsideRange, - ExpectedValue, - }; - using ParseCommandLineParametersError = ErrorWithCode; - - std::expected parse_command_line_parameters(int argc, char const* argv[]) noexcept; - -} // namespace PNG_CICP_Editor - -#endif // #ifndef PNG_CICP_EDITOR_COMMANDLINEPARAMETERS_HPP +// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef PNG_CICP_EDITOR_COMMANDLINEPARAMETERS_HPP +#define PNG_CICP_EDITOR_COMMANDLINEPARAMETERS_HPP + +#include +#include +#include +#include + +#include "Actions.hpp" +#include "Error.hpp" + +namespace PNG_CICP_Editor { + + /* + class foo { + Actions action_type_; + std::optional color_primaries_; + std::optional transfer_function_; + uint8_t matrix_coefficients_; + uint8_t video_full_range_flag_; + std::string file_path_; + }; + */ + + enum class ParseCommandLineParametersErrorCode { + UnrecognizedParameter, + ValueOutsideRange, + ExpectedValue, + }; + using ParseCommandLineParametersError = ErrorWithCode; + + std::expected parse_command_line_parameters(int argc, char const* argv[]) noexcept; + +} // namespace PNG_CICP_Editor + +#endif // #ifndef PNG_CICP_EDITOR_COMMANDLINEPARAMETERS_HPP diff --git a/Code/png_cicp_editor/EntryPoint.cpp b/Code/png_cicp_editor/EntryPoint.cpp index 1814e9f..02c94b1 100644 --- a/Code/png_cicp_editor/EntryPoint.cpp +++ b/Code/png_cicp_editor/EntryPoint.cpp @@ -1,39 +1,20 @@ -// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "CommandLineParameters.hpp" -#include "Error.hpp" - - -int main(int argc, char const* argv[]) noexcept { - // Parse the command line parameters - auto command_line_parameters = PNG_CICP_Editor::parse_command_line_parameters(argc, argv); - if (!command_line_parameters.has_value()) { - print_error(command_line_parameters.error()); - return 1; - } - - switch (command_line_parameters->action_type_) { - case PNG_CICP_Editor::Actions::Version: - command_line_parameters->action_.version_(); - break; - case PNG_CICP_Editor::Actions::Help: - command_line_parameters->action_.help_(); - break; - case PNG_CICP_Editor::Actions::License: - command_line_parameters->action_.license_(); - break; - case PNG_CICP_Editor::Actions::Add: - command_line_parameters->action_.add_(); - break; - case PNG_CICP_Editor::Actions::Overwrite: - command_line_parameters->action_.overwrite_(); - break; - case PNG_CICP_Editor::Actions::Remove: - command_line_parameters->action_.remove_(); - break; - } - - return 0; +// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "Actions.hpp" +#include "CommandLineParameters.hpp" +#include "Error.hpp" + +int main(int argc, char const* argv[]) noexcept { + // Parse the command line parameters + auto command_line_parameters = PNG_CICP_Editor::parse_command_line_parameters(argc, argv); + if (!command_line_parameters.has_value()) { + print_error(command_line_parameters.error()); + return 1; + } + + std::visit(PNG_CICP_Editor::ActionExecutor(), *command_line_parameters); + + return 0; } \ No newline at end of file diff --git a/Code/tests/ActionsTest.cpp b/Code/tests/ActionsTest.cpp index 22ad1a4..003cb04 100644 --- a/Code/tests/ActionsTest.cpp +++ b/Code/tests/ActionsTest.cpp @@ -1,57 +1,57 @@ -// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "ActionsTest.hpp" - -#include - -#include -#include - -#include -#include - -namespace { - - auto cicp = PNG_CICP_Editor::CICP{ 1, 2, 3, 4 }; - auto file_path = std::string{ R"(C:\images\test.png)" }; - -} // Anonymous namespace - -namespace PNG_CICP_Editor { - - void RunActionsTestSuite() - { - max::Testing::CoutResultPolicy ResultPolicy; - auto ActionsTestSuite = max::Testing::TestSuite< max::Testing::CoutResultPolicy >{ "Actions test suite", std::move(ResultPolicy) }; - - // TODO: Test the operator()s work. Maybe mocks here? - - ActionsTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "AddAction ctor assigns members", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - auto add_action = AddAction{ cicp, file_path }; - - CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_ == cicp); - CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == file_path); - } - }); - - ActionsTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "OverwriteAction ctor assigns members", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - auto overwrite_action = OverwriteAction{ cicp, file_path }; - - CurrentTest.MAX_TESTING_ASSERT(overwrite_action.cicp_ == cicp); - CurrentTest.MAX_TESTING_ASSERT(overwrite_action.file_path_ == file_path); - } - }); - - ActionsTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "RemoveAction ctor assigns members", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - auto remove_action = RemoveAction{ file_path }; - - CurrentTest.MAX_TESTING_ASSERT(remove_action.file_path_ == file_path); - } - }); - - ActionsTestSuite.RunTests(); - } - -} // namespace PNG_CICP_Editor +// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "ActionsTest.hpp" + +#include + +#include +#include + +#include +#include + +namespace { + + auto cicp = PNG_CICP_Editor::CICP{ 1, 2, 3, 4 }; + auto file_path = std::string{ R"(C:\images\test.png)" }; + +} // Anonymous namespace + +namespace PNG_CICP_Editor { + + void RunActionsTestSuite() + { + max::Testing::CoutResultPolicy ResultPolicy; + auto ActionsTestSuite = max::Testing::TestSuite< max::Testing::CoutResultPolicy >{ "Actions test suite", std::move(ResultPolicy) }; + + ActionsTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "AddAction ctor assigns members", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + auto add_action = AddAction{ cicp, file_path }; + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_ == cicp); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == file_path); + } + }); + + ActionsTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "OverwriteAction ctor assigns members", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + auto overwrite_action = OverwriteAction{ cicp, file_path }; + + CurrentTest.MAX_TESTING_ASSERT(overwrite_action.cicp_ == cicp); + CurrentTest.MAX_TESTING_ASSERT(overwrite_action.file_path_ == file_path); + } + }); + + ActionsTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "RemoveAction ctor assigns members", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + auto remove_action = RemoveAction{ file_path }; + + CurrentTest.MAX_TESTING_ASSERT(remove_action.file_path_ == file_path); + } + }); + + // TODO: Add ActionExecutor tests. + + ActionsTestSuite.RunTests(); + } + +} // namespace PNG_CICP_Editor diff --git a/Code/tests/CommandLineParametersTest.cpp b/Code/tests/CommandLineParametersTest.cpp index 3359dc0..c3a50ec 100644 --- a/Code/tests/CommandLineParametersTest.cpp +++ b/Code/tests/CommandLineParametersTest.cpp @@ -1,562 +1,580 @@ -// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "CommandLineParametersTest.hpp" - -#include -#include -#include - -namespace { - - // actions - static constinit char const* version = "--version"; - static constinit char const* v_string = "-v"; - static constinit char const* help = "--help"; - static constinit char const* h_string = "-h"; - static constinit char const* license = "--license"; - static constinit char const* add = "add"; - static constinit char const* overwrite = "overwrite"; - static constinit char const* remove_string = "remove"; - - static constinit char const* preset = "--preset"; - static constinit char const* narrow = "--narrow"; - static constinit char const* full = "--full"; - static constinit char const* p_string = "-p"; - static constinit char const* n_string = "-n"; - static constinit char const* f_string = "-f"; - - static constinit char const* color_primaries = "--color_primaries"; - static constinit char const* transfer_function = "--transfer_function"; - static constinit char const* matrix_coefficients = "--matrix_coefficients"; - static constinit char const* video_full_range_flag = "--video_full_range_flag"; - - static constinit char const* program_name = "png_cicp_editor.exe"; - static constinit char const* test_image_path = "C:\\test\\image.png"; - -} // anonymous namespace - -namespace PNG_CICP_Editor { - - void RunCommandLineParametersTestSuite() - { - max::Testing::CoutResultPolicy ResultPolicy; - auto CommandLineParametersTestSuite = max::Testing::TestSuite< max::Testing::CoutResultPolicy >{ "CommandLineParameters test suite", std::move(ResultPolicy) }; - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "ParseCommandLineParametersError ctor assigns members", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - auto error = ParseCommandLineParametersError{ ParseCommandLineParametersErrorCode::UnrecognizedParameter, { preset } }; - - CurrentTest.MAX_TESTING_ASSERT(error.error_code_ == ParseCommandLineParametersErrorCode::UnrecognizedParameter); - CurrentTest.MAX_TESTING_ASSERT(error.output_messages_.size() == 1); - CurrentTest.MAX_TESTING_ASSERT(error.output_messages_[0] == preset); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--version returns correct action", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 2; - char const* argv[argc] = { program_name, version }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Version); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "-v returns correct action", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 2; - char const* argv[argc] = { program_name, v_string }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Version); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--help returns correct action", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 2; - char const* argv[argc] = { program_name, help }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Help); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "-h returns correct action", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 2; - char const* argv[argc] = { program_name, h_string }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Help); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--license returns correct action", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 2; - char const* argv[argc] = { program_name, license }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::License); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset bt.601-pal returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, preset, "bt.601-pal", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 5); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 6); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset bt.601-ntsc returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, preset, "bt.601-ntsc", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 6); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 6); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset bt.709 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, preset, "bt.709", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset linear-light-srgb returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, preset, "linear-light-srgb", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 8); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset srgb returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, preset, "srgb", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 13); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset bt.2020-10-bit returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, preset, "bt.2020-10-bit", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 9); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 14); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset bt.2020-12-bit returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, preset, "bt.2020-12-bit", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 9); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 15); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset bt.2100-pq returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, preset, "bt.2100-pq", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 9); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 16); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset bt.2100-hlg returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, preset, "bt.2100-hlg", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 9); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 18); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset dci-p3 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, preset, "dci-p3", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 11); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 17); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset display-p3 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, preset, "display-p3", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 12); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 13); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset p3-d65-pq returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, preset, "p3-d65-pq", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 12); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 16); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "-p bt.709 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, p_string, "bt.709", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--color_primaries 42 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, color_primaries, "42", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 42); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--transfer_function 42 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, transfer_function, "42", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 42); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--matrix_coefficients 42 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, matrix_coefficients, "42", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 42); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--video_full_range_flag 42 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, video_full_range_flag, "42", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 42); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--color_primaries 42 --transfer_function 43 --matrix_coefficients 44 --video_full_range_flag 45 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 11; - char const* argv[argc] = { program_name, add, color_primaries, "42", transfer_function, "43", matrix_coefficients, "44", video_full_range_flag, "45", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 42); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 43); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 44); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 45); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset srgb --color_primaries 42 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 7; - char const* argv[argc] = { program_name, add, preset, "srgb", color_primaries, "42", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 42); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 13); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "-p srgb --narrow returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 6; - char const* argv[argc] = { program_name, add, preset, "srgb", narrow, test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 13); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "-p srgb -n returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 6; - char const* argv[argc] = { program_name, add, preset, "srgb", n_string, test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 13); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "-p srgb -n --full returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 7; - char const* argv[argc] = { program_name, add, preset, "srgb", n_string, full, test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 13); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "-p srgb -n -f returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 7; - char const* argv[argc] = { program_name, add, preset, "srgb", n_string, f_string, test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Add); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.color_primaries_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.transfer_function_ == 13); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.add_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "overwrite -p srgb returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, overwrite, preset, "srgb", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Overwrite); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.overwrite_.cicp_.color_primaries_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.overwrite_.cicp_.transfer_function_ == 13); - CurrentTest.MAX_TESTING_ASSERT(result->action_.overwrite_.cicp_.matrix_coefficients_ == 0); - CurrentTest.MAX_TESTING_ASSERT(result->action_.overwrite_.cicp_.video_full_range_flag_ == 1); - CurrentTest.MAX_TESTING_ASSERT(result->action_.overwrite_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "remove return correct action and file path", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 3; - char const* argv[argc] = { program_name, remove_string, test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(result.has_value()); - - CurrentTest.MAX_TESTING_ASSERT(result->action_type_ == Actions::Remove); - - CurrentTest.MAX_TESTING_ASSERT(result->action_.remove_.file_path_ == test_image_path); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "Missing file path errors", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 3; - char const* argv[argc] = { program_name, preset, "srgb" }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(!result.has_value()); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "Unrecognized flag errors", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 2; - char const* argv[argc] = { program_name, "not-an-action" }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(!result.has_value()); - CurrentTest.MAX_TESTING_ASSERT(result.error().error_code_ == ParseCommandLineParametersErrorCode::UnrecognizedParameter); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "Unrecognized flag errors", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 4; - char const* argv[argc] = { program_name, add, "--not-a-flag", "0" }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(!result.has_value()); - CurrentTest.MAX_TESTING_ASSERT(result.error().error_code_ == ParseCommandLineParametersErrorCode::UnrecognizedParameter); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "Unrecognized preset errors", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, preset, "foo", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(!result.has_value()); - CurrentTest.MAX_TESTING_ASSERT(result.error().error_code_ == ParseCommandLineParametersErrorCode::UnrecognizedParameter); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "Non-number values error", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, color_primaries, "foo", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(!result.has_value()); - CurrentTest.MAX_TESTING_ASSERT(result.error().error_code_ == ParseCommandLineParametersErrorCode::UnrecognizedParameter); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "Negative number value errors", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, color_primaries, "-1", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(!result.has_value()); - CurrentTest.MAX_TESTING_ASSERT(result.error().error_code_ == ParseCommandLineParametersErrorCode::ValueOutsideRange); - } - }); - - CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "Overflowing number value errors", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { - const int argc = 5; - char const* argv[argc] = { program_name, add, color_primaries, "256", test_image_path }; - auto result = parse_command_line_parameters(argc, argv); - CurrentTest.MAX_TESTING_ASSERT(!result.has_value()); - CurrentTest.MAX_TESTING_ASSERT(result.error().error_code_ == ParseCommandLineParametersErrorCode::ValueOutsideRange); - } - }); - - CommandLineParametersTestSuite.RunTests(); - } - -} // namespace PNG_CICP_Editor +// Copyright 2025, The png_cicp_editor Contributors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "CommandLineParametersTest.hpp" + +#include +#include +#include + +namespace { + + // actions + static constinit char const* version = "--version"; + static constinit char const* v_string = "-v"; + static constinit char const* help = "--help"; + static constinit char const* h_string = "-h"; + static constinit char const* license = "--license"; + static constinit char const* add = "add"; + static constinit char const* overwrite = "overwrite"; + static constinit char const* remove_string = "remove"; + + static constinit char const* preset = "--preset"; + static constinit char const* narrow = "--narrow"; + static constinit char const* full = "--full"; + static constinit char const* p_string = "-p"; + static constinit char const* n_string = "-n"; + static constinit char const* f_string = "-f"; + + static constinit char const* color_primaries = "--color_primaries"; + static constinit char const* transfer_function = "--transfer_function"; + static constinit char const* matrix_coefficients = "--matrix_coefficients"; + static constinit char const* video_full_range_flag = "--video_full_range_flag"; + + static constinit char const* program_name = "png_cicp_editor.exe"; + static constinit char const* test_image_path = "C:\\test\\image.png"; + +} // anonymous namespace + +namespace PNG_CICP_Editor { + + void RunCommandLineParametersTestSuite() + { + max::Testing::CoutResultPolicy ResultPolicy; + auto CommandLineParametersTestSuite = max::Testing::TestSuite< max::Testing::CoutResultPolicy >{ "CommandLineParameters test suite", std::move(ResultPolicy) }; + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "ParseCommandLineParametersError ctor assigns members", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + auto error = ParseCommandLineParametersError{ ParseCommandLineParametersErrorCode::UnrecognizedParameter, { preset } }; + + CurrentTest.MAX_TESTING_ASSERT(error.error_code_ == ParseCommandLineParametersErrorCode::UnrecognizedParameter); + CurrentTest.MAX_TESTING_ASSERT(error.output_messages_.size() == 1); + CurrentTest.MAX_TESTING_ASSERT(error.output_messages_[0] == preset); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--version returns correct action", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 2; + char const* argv[argc] = { program_name, version }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + try { + std::get(*result); + } + catch (const std::bad_variant_access&) { + CurrentTest.MAX_TESTING_ASSERT(false); + } + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "-v returns correct action", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 2; + char const* argv[argc] = { program_name, v_string }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + try { + std::get(*result); + } + catch (const std::bad_variant_access&) { + CurrentTest.MAX_TESTING_ASSERT(false); + } + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--help returns correct action", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 2; + char const* argv[argc] = { program_name, help }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + try { + std::get(*result); + } + catch (const std::bad_variant_access&) { + CurrentTest.MAX_TESTING_ASSERT(false); + } + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "-h returns correct action", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 2; + char const* argv[argc] = { program_name, h_string }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + try { + std::get(*result); + } + catch (const std::bad_variant_access&) { + CurrentTest.MAX_TESTING_ASSERT(false); + } + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--license returns correct action", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 2; + char const* argv[argc] = { program_name, license }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + try { + std::get(*result); + } + catch (const std::bad_variant_access&) { + CurrentTest.MAX_TESTING_ASSERT(false); + } + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset bt.601-pal returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, preset, "bt.601-pal", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + try { + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 5); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 6); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + } + catch (const std::bad_variant_access&) { + CurrentTest.MAX_TESTING_ASSERT(false); + } + + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset bt.601-ntsc returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, preset, "bt.601-ntsc", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 6); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 6); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset bt.709 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, preset, "bt.709", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset linear-light-srgb returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, preset, "linear-light-srgb", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 8); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset srgb returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, preset, "srgb", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 13); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset bt.2020-10-bit returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, preset, "bt.2020-10-bit", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 9); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 14); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset bt.2020-12-bit returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, preset, "bt.2020-12-bit", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 9); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 15); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset bt.2100-pq returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, preset, "bt.2100-pq", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 9); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 16); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset bt.2100-hlg returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, preset, "bt.2100-hlg", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 9); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 18); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset dci-p3 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, preset, "dci-p3", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 11); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 17); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset display-p3 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, preset, "display-p3", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 12); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 13); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset p3-d65-pq returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, preset, "p3-d65-pq", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 12); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 16); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "-p bt.709 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, p_string, "bt.709", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--color_primaries 42 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, color_primaries, "42", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 42); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--transfer_function 42 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, transfer_function, "42", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 42); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--matrix_coefficients 42 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, matrix_coefficients, "42", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 42); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--video_full_range_flag 42 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, video_full_range_flag, "42", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 42); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--color_primaries 42 --transfer_function 43 --matrix_coefficients 44 --video_full_range_flag 45 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 11; + char const* argv[argc] = { program_name, add, color_primaries, "42", transfer_function, "43", matrix_coefficients, "44", video_full_range_flag, "45", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 42); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 43); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 44); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 45); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "--preset srgb --color_primaries 42 returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 7; + char const* argv[argc] = { program_name, add, preset, "srgb", color_primaries, "42", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 42); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 13); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "-p srgb --narrow returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 6; + char const* argv[argc] = { program_name, add, preset, "srgb", narrow, test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 13); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "-p srgb -n returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 6; + char const* argv[argc] = { program_name, add, preset, "srgb", n_string, test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 13); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "-p srgb -n --full returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 7; + char const* argv[argc] = { program_name, add, preset, "srgb", n_string, full, test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 13); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "-p srgb -n -f returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 7; + char const* argv[argc] = { program_name, add, preset, "srgb", n_string, f_string, test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto add_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.color_primaries_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.transfer_function_ == 13); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(add_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(add_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "overwrite -p srgb returns correct CICP values", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, overwrite, preset, "srgb", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto overwrite_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(overwrite_action.cicp_.color_primaries_ == 1); + CurrentTest.MAX_TESTING_ASSERT(overwrite_action.cicp_.transfer_function_ == 13); + CurrentTest.MAX_TESTING_ASSERT(overwrite_action.cicp_.matrix_coefficients_ == 0); + CurrentTest.MAX_TESTING_ASSERT(overwrite_action.cicp_.video_full_range_flag_ == 1); + CurrentTest.MAX_TESTING_ASSERT(overwrite_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "remove return correct action and file path", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 3; + char const* argv[argc] = { program_name, remove_string, test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(result.has_value()); + + CurrentTest.MAX_TESTING_ASSERT(std::holds_alternative(*result)); + auto remove_action = std::move(std::get(*result)); + + CurrentTest.MAX_TESTING_ASSERT(remove_action.file_path_ == test_image_path); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "Missing file path errors", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 3; + char const* argv[argc] = { program_name, preset, "srgb" }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(!result.has_value()); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "Unrecognized flag errors", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 2; + char const* argv[argc] = { program_name, "not-an-action" }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(!result.has_value()); + CurrentTest.MAX_TESTING_ASSERT(result.error().error_code_ == ParseCommandLineParametersErrorCode::UnrecognizedParameter); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "Unrecognized flag errors", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 4; + char const* argv[argc] = { program_name, add, "--not-a-flag", "0" }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(!result.has_value()); + CurrentTest.MAX_TESTING_ASSERT(result.error().error_code_ == ParseCommandLineParametersErrorCode::UnrecognizedParameter); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "Unrecognized preset errors", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, preset, "foo", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(!result.has_value()); + CurrentTest.MAX_TESTING_ASSERT(result.error().error_code_ == ParseCommandLineParametersErrorCode::UnrecognizedParameter); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "Non-number values error", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, color_primaries, "foo", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(!result.has_value()); + CurrentTest.MAX_TESTING_ASSERT(result.error().error_code_ == ParseCommandLineParametersErrorCode::UnrecognizedParameter); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "Negative number value errors", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, color_primaries, "-1", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(!result.has_value()); + CurrentTest.MAX_TESTING_ASSERT(result.error().error_code_ == ParseCommandLineParametersErrorCode::ValueOutsideRange); + }}); + + CommandLineParametersTestSuite.AddTest(max::Testing::Test< max::Testing::CoutResultPolicy >{ "Overflowing number value errors", [](max::Testing::Test< max::Testing::CoutResultPolicy >& CurrentTest, max::Testing::CoutResultPolicy const& ResultPolicy) { + const int argc = 5; + char const* argv[argc] = { program_name, add, color_primaries, "256", test_image_path }; + auto result = parse_command_line_parameters(argc, argv); + CurrentTest.MAX_TESTING_ASSERT(!result.has_value()); + CurrentTest.MAX_TESTING_ASSERT(result.error().error_code_ == ParseCommandLineParametersErrorCode::ValueOutsideRange); + }}); + + CommandLineParametersTestSuite.RunTests(); + } + +} // namespace PNG_CICP_Editor