diff --git a/imgui.ini b/imgui.ini index e8673b1..5b3f7e9 100644 --- a/imgui.ini +++ b/imgui.ini @@ -14,6 +14,11 @@ Collapsed=0 [Window][Main] Pos=0,0 -Size=1720,1148 +Size=1654,1186 +Collapsed=0 + +[Window][Help] +Pos=136,255 +Size=177,114 Collapsed=0 diff --git a/lib/argparse.hpp b/lib/argparse.hpp new file mode 100644 index 0000000..97b67d4 --- /dev/null +++ b/lib/argparse.hpp @@ -0,0 +1,586 @@ +#pragma once +// +// @author : Morris Franken +// https://github.com/morrisfranken/argparse +// +// Permission is hereby granted, free of charge, to any person or organization +// obtaining a copy of the software and accompanying documentation covered by +// this license (the "Software") to use, reproduce, display, distribute, +// execute, and transmit the Software, and to prepare derivative works of the +// Software, and to permit third-parties to whom the Software is furnished to +// do so, all subject to the following: +// +// The copyright notices in the Software and this entire statement, including +// the above license grant, this restriction and the following disclaimer, +// must be included in all copies of the Software, in whole or in part, and +// all derivative works of the Software, unless such copies or derivative +// works are solely in the form of machine-executable object code generated by +// a source language processor. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +// SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +// FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +// ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +// DEALINGS IN THE SOFTWARE. +#include // for isdigit, tolower +#include +#include // for size_t, exit +#include // for max, transform, copy, min +#include // for operator<<, setw +#include // for operator<<, basic_ostream, endl, ostream +#include // for ostream_iterator +#include // for operator!=, map, _Rb_tree_iterator +#include // for allocator, shared_ptr, __shared_ptr_ac... +#include // for optional, nullopt +#include // for runtime_error, invalid_argument +#include // for getting program_name from path +#include // for string, operator+, basic_string, char_... +#include // for declval, false_type, true_type, is_enum +#include // for move, pair +#include // for vector + +#if __has_include() +#include // for enum_entries +#define HAS_MAGIC_ENUM +#endif + +#define ARGPARSE_VERSION 4 + +namespace argparse { + class Args; + using std::cout, std::cerr, std::endl, std::setw, std::size_t; + + template struct is_vector : public std::false_type {}; + template struct is_vector> : public std::true_type {}; + + template struct is_optional : public std::false_type {}; + template struct is_optional> : public std::true_type {}; + + template struct is_shared_ptr : public std::false_type {}; + template struct is_shared_ptr> : public std::true_type {}; + + template struct has_ostream_operator : std::false_type {}; + template struct has_ostream_operator() << std::declval()))> : std::true_type {}; + + inline std::string bold(const std::string& input_str) { +#ifdef _WIN32 + return input_str; // no bold for windows +#else + return "\033[1m" + input_str + "\033[0m"; +#endif + } + + template std::string toString(const T &v) { + if constexpr (has_ostream_operator::value) { + return static_cast((std::ostringstream() << std::boolalpha << v)).str(); // https://github.com/stan-dev/math/issues/590#issuecomment-550122627 + } else { + return "unknown"; + } + } + + std::vector inline split(const std::string &str) { + std::vector splits; + std::stringstream ss(str); + std::string key; + while (std::getline(ss, key, ',')) { + if (!key.empty() && key.back() == '\0') + key.pop_back(); // last variables contain a '\0', which is unexpected when comparing to raw string, e.g. value == "test" will fail when the last character is '\0'. Therefore we can remove it + splits.emplace_back(std::move(key)); + } + return splits; + } + + template std::string to_lower(const T &str_) { // both std::string and std::basic_string_view (for magic_enum) are using to_lower + std::string str(str_.size(), '\0'); + std::transform(str_.begin(), str_.end(), str.begin(), ::tolower); + return str; + } + + template inline T get(const std::string &v); + template<> inline std::string get(const std::string &v) { return v; } + template<> inline char get(const std::string &v) { return v.empty()? throw std::invalid_argument("empty string") : v.size() > 1? v.substr(0,2) == "0x"? (char)std::stoul(v, nullptr, 16) : (char)std::stoi(v) : v[0]; } + template<> inline int get(const std::string &v) { return std::stoi(v); } + template<> inline short get(const std::string &v) { return std::stoi(v); } + template<> inline long get(const std::string &v) { return std::stol(v); } + template<> inline bool get(const std::string &v) { return to_lower(v) == "true" || v == "1"; } + template<> inline float get(const std::string &v) { return std::stof(v); } + template<> inline double get(const std::string &v) { return std::stod(v); } + template<> inline unsigned char get(const std::string &v) { return get(v); } + template<> inline unsigned int get(const std::string &v) { return std::stoul(v); } + template<> inline unsigned short get(const std::string &v) { return std::stoul(v); } + template<> inline unsigned long get(const std::string &v) { return std::stoul(v); } + + template inline T get(const std::string &v) { // remaining types + if constexpr (is_vector::value) { + const std::vector splitted = split(v); + T res(splitted.size()); + if (!v.empty()) + std::transform (splitted.begin(), splitted.end(), res.begin(), get); + return res; + } else if constexpr (std::is_pointer::value) { + return new typename std::remove_pointer::type(get::type>(v)); + } else if constexpr (is_shared_ptr::value) { + return std::make_shared(get(v)); + } else if constexpr (is_optional::value) { + return get(v); + } else if constexpr (std::is_enum::value) { // case-insensitive enum conversion +#ifdef HAS_MAGIC_ENUM + constexpr auto& enum_entries = magic_enum::enum_entries(); + const std::string lower_str = to_lower(v); + for (const auto &[value, name] : enum_entries) { + if (to_lower(name) == lower_str) + return value; + } + std::string error = "enum is only accepting ["; + for (size_t i = 0; i < enum_entries.size(); i++) + error += (i==0? "" : ", ") + to_lower(enum_entries[i].second); + error += "]"; + throw std::runtime_error(error); +#else + throw std::runtime_error("Enum not supported, please install magic_enum (https://github.com/Neargye/magic_enum)"); +#endif + } else { + return T(v); + } + } + + struct ConvertBase { + virtual ~ConvertBase() = default; + virtual void convert(const std::string &v) = 0; + virtual void set_default(const std::unique_ptr &default_value, const std::string &default_string) = 0; + [[nodiscard]] virtual size_t get_type_id() const = 0; + [[nodiscard]] virtual std::string get_allowed_entries() const = 0; + }; + + template struct ConvertType : public ConvertBase { + T data; + ~ConvertType() override = default; + ConvertType() : ConvertBase() {}; + explicit ConvertType(const T &value) : ConvertBase(), data(value) {}; + + void convert(const std::string &v) override { + data = get(v); + } + + void set_default(const std::unique_ptr &default_value, const std::string &default_string) override { + if (this->get_type_id() == default_value->get_type_id()) // When the types do not match exactly. resort to string conversion + data = ((ConvertType*)(default_value.get()))->data; + else + data = get(default_string); + } + + [[nodiscard]] size_t get_type_id() const override { + return typeid(T).hash_code(); + } + + [[nodiscard]] std::string get_allowed_entries() const override { + std::stringstream ss; + +#ifdef HAS_MAGIC_ENUM + if constexpr (std::is_enum::value) { + for (const auto &[value, name] : magic_enum::enum_entries()) { + ss << to_lower(name) << ", "; + } + } +#endif + + return ss.str(); + } + }; + + struct Entry { + enum ARG_TYPE {ARG, KWARG, FLAG} type; + + Entry(ARG_TYPE type, const std::string& key, std::string help, std::optional implicit_value=std::nullopt) : + type(type), + keys_(split(key)), + help(std::move(help)), + implicit_value_(std::move(implicit_value)) { + } + + // Allow both string inputs and direct-type inputs. Where a string-input will be converted like it would when using the commandline, and the direct approach is to simply use the value provided. + template Entry &set_default(const T &default_value) { + this->default_str_ = toString(default_value); + if constexpr (!(std::is_array::value || std::is_same::type, char>::value)) { + data_default = std::make_unique>(default_value); + } + return *this; + } + + Entry &multi_argument() { + _is_multi_argument = true; + return *this; + } + + // Magically convert the value string to the requested type + template operator T&() { + // Automatically set the default to nullptr for pointer types and empty for optional types + if constexpr (is_optional::value || std::is_pointer::value || is_shared_ptr::value) { + if (!default_str_.has_value()) { + default_str_ = "none"; + if constexpr(is_optional::value) { + data_default = std::make_unique> (T{std::nullopt}); + } else { + data_default = std::make_unique> ((T) nullptr); + } + } + } + + datap = std::make_unique>(); + return ((ConvertType*)(datap.get()))->data; + } + + // Force an ambiguous error when not using a reference. + template operator T() {} // When you get here because you received an error, make sure all parameters of argparse are references (e.g. with `&`) + + private: + std::vector keys_; + std::string help; + std::optional value_; + std::optional implicit_value_; + std::optional default_str_; + std::string error; + std::unique_ptr datap; + std::unique_ptr data_default; + bool _is_multi_argument = false; + bool is_set_by_user = true; + + [[nodiscard]] std::string _get_keys() const { + std::stringstream ss; + for (size_t i = 0; i < keys_.size(); i++) + ss << (i? "," : "") << (type == ARG? "" : (keys_[i].size() > 1 ? "--" : "-")) + keys_[i]; + return ss.str(); + } + + void _convert(const std::string &value) { + try { + this->value_ = value; + datap->convert(value); + } catch (const std::invalid_argument &e) { + error = "Invalid argument, could not convert \"" + value + "\" for " + _get_keys() + " (" + help + ")"; + } catch (const std::runtime_error &e) { + error = "Invalid argument \"" + value + "\" for " + _get_keys() + " (" + help + "). Error: " + e.what(); + } + } + + void _apply_default() { + is_set_by_user = false; + if (data_default != nullptr) { + value_ = *default_str_; // for printing + datap->set_default(data_default, *default_str_); + } else if (default_str_.has_value()) { // in cases where a string is provided to the `set_default` function + _convert(default_str_.value()); + } else { + error = "Argument missing: " + _get_keys() + " (" + help + ")"; + } + } + + [[nodiscard]] std::string info() const { + const std::string allowed_entries = datap->get_allowed_entries(); + const std::string default_value = default_str_.has_value() ? "default: " + *default_str_ : "required"; + const std::string implicit_value = implicit_value_.has_value() ? "implicit: \"" + *implicit_value_ + "\", ": ""; + const std::string allowed_value = !allowed_entries.empty()? "allowed: <" + allowed_entries.substr(0, allowed_entries.size()-2) + ">, ": ""; + return " [" + allowed_value + implicit_value + default_value + "]"; + } + + friend class Args; + }; + + struct SubcommandEntry { + std::shared_ptr subargs; + std::string subcommand_name; + + explicit SubcommandEntry(std::string subcommand_name) : subcommand_name(std::move(subcommand_name)) {} + + template operator T &() { + static_assert(std::is_base_of_v, "Subcommand type must be a derivative of argparse::Args"); + + std::shared_ptr res = std::make_shared(); + res->program_name = subcommand_name; + subargs = res; + return *(T*)(subargs.get()); + } + + // Force an ambiguous error when not using a reference. + template operator T() {} // When you get here because you received an error, make sure all parameters of argparse are references (e.g. with `&`) + }; + + class Args { + private: + size_t _arg_idx = 0; + std::vector params; + std::vector> all_entries; + std::map> kwarg_entries; + std::vector> arg_entries; + std::map> subcommand_entries; + + public: + std::string program_name; + bool is_valid = false; + + virtual ~Args() = default; + + /* Add a positional argument, the order in which it is defined equals the order in which they are being read. + * help : Description of the variable + * + * Returns a reference to the Entry, which will collapse into the requested type in `Entry::operator T()` + */ + Entry &arg(const std::string &help) { + return arg("arg_" + std::to_string(_arg_idx), help); + } + + /* Add a *named* positional argument, the order in which it is defined equals the order in which they are being read. + * key : The name of the argument, otherwise arg_ will be used + * help : Description of the variable + * + * Returns a reference to the Entry, which will collapse into the requested type in `Entry::operator T()` + */ + Entry &arg(const std::string& key, const std::string &help) { + std::shared_ptr entry = std::make_shared(Entry::ARG, key, help); + // Increasing _arg_idx, so that arg2 will be arg_2, irregardless of whether it is preceded by other positional arguments + _arg_idx++; + arg_entries.emplace_back(entry); + all_entries.emplace_back(entry); + return *entry; + } + + /* Add a Key-Worded argument that takes a variable. + * key : A comma-separated string, e.g. "k,key", which denotes the short (-k) and long(--key) keys_ + * help : Description of the variable + * implicit_value : Implicit values are used when no value is provided. + * + * Returns a reference to the Entry, which will collapse into the requested type in `Entry::operator T()` + */ + Entry &kwarg(const std::string &key, const std::string &help, const std::optional& implicit_value=std::nullopt) { + std::shared_ptr entry = std::make_shared(Entry::KWARG, key, help, implicit_value); + all_entries.emplace_back(entry); + for (const std::string &k : entry->keys_) { + kwarg_entries[k] = entry; + } + return *entry; + } + + /* Add a flag which will be false by default. + * key : A comma-separated string, e.g. "k,key", which denotes the short (-k) and long(--key) keys_ + * help : Description of the variable + * + * Returns reference to Entry like kwarg + */ + Entry &flag(const std::string &key, const std::string &help) { + return kwarg(key, help, "true").set_default(false); + } + + /* Add a a subcommand + * command : name of the subcommand, e.g. 'commit', if you wish to implement a function like 'git commit' + * + * Returns a reference to the Entry, which will collapse into the requested type in `Entry::operator T()` + * Expected type *Must* be an std::shared_ptr of derivative of the argparse::Args class + */ + SubcommandEntry &subcommand(const std::string &command) { + std::shared_ptr entry = std::make_shared(command); + subcommand_entries[command] = entry; + return *entry; + } + + virtual void welcome() {} // Allow to overwrite the `welcome` function to add a welcome-message to the help output + virtual void help() { + welcome(); + cout << "Usage: " << program_name << " "; + for (const auto &entry : arg_entries) + cout << entry->keys_[0] << ' '; + cout << " [options...]"; + if (!subcommand_entries.empty()) { + cout << " [SUBCOMMAND: "; + for (const auto &[subcommand, subentry]: subcommand_entries) { + cout << subcommand << ", "; + } + cout << "]"; + } + cout << endl; + for (const auto &entry : arg_entries) { + cout << setw(17) << entry->keys_[0] << " : " << entry->help << entry->info() << endl; + } + + cout << endl << "Options:" << endl; + for (const auto &entry : all_entries) { + if (entry->type != Entry::ARG) { + cout << setw(17) << entry->_get_keys() << " : " << entry->help << entry->info() << endl; + } + } + + for (const auto &[subcommand, subentry] : subcommand_entries) { + cout << endl << endl << bold("Subcommand: ") << bold(subcommand) << endl; + subentry->subargs->help(); + } + } + + void validate(const bool &raise_on_error) { + for (const auto &entry : all_entries) { + if (!entry->error.empty()) { + if (raise_on_error) { + throw std::runtime_error(entry->error); + } else { + std::cerr << entry->error << std::endl; + exit(-1); + } + } + } + } + + /* parse all parameters and also check for the help_flag which was set in this constructor + * Upon error, it will print the error and exit immediately if validation_action is ValidationAction::EXIT_ON_ERROR + */ + void parse(int argc, const char* const *argv, const bool &raise_on_error) { + auto parse_subcommands = [&]() -> int { + for (int i = 1; i < argc; i++) { + for (auto &[subcommand, subentry] : subcommand_entries) { + if (subcommand == argv[i]) { + subentry->subargs->parse(argc - i, argv + i, raise_on_error); + return i; + } + } + } + return argc; + }; + argc = parse_subcommands(); // argc_ is the number of arguments that should be parsed after the subcommand has finished parsing + + program_name = std::filesystem::path(argv[0]).stem().string(); + params = std::vector(argv + 1, argv + argc); + + bool& _help = flag("?,help", "print help"); + + auto is_value = [&](const size_t &i) -> bool { + return params.size() > i && (params[i][0] != '-' || (params[i].size() > 1 && std::isdigit(params[i][1]))); // check for number to not accidentally mark negative numbers as non-parameter + }; + auto parse_param = [&](size_t &i, const std::string &key, const bool is_short, const std::optional &equal_value=std::nullopt) { + auto itt = kwarg_entries.find(key); + if (itt != kwarg_entries.end()) { + auto &entry = itt->second; + if (equal_value.has_value()) { + entry->_convert(equal_value.value()); + } else if (entry->implicit_value_.has_value()) { + entry->_convert(*entry->implicit_value_); + } else if (!is_short) { // short values are not allowed to look ahead for the next parameter + if (is_value(i + 1)) { + std::string value = params[++i]; + if (entry->_is_multi_argument) { + while (is_value(i + 1)) + value += "," + params[++i]; + } + entry->_convert(value); + } else if (entry->_is_multi_argument) { + entry->_convert(""); // for multiargument parameters, return an empty vector when not passing any more values + } else { + entry->error = "No value provided for: " + key; + } + } else { + entry->error = "No value provided for: " + key; + } + } else { + cerr << "unrecognised commandline argument: " << key << endl; + } + }; + auto add_param = [&](size_t &i, const size_t &start) { + size_t eq_idx = params[i].find('='); // check if value was passed using the '=' sign + if (eq_idx != std::string::npos) { // key/value from = notation + std::string key = params[i].substr(start, eq_idx - start); + std::string value = params[i].substr(eq_idx + 1); + parse_param(i, key, false, value); + } else { + std::string key = std::string(params[i].substr(start)); + parse_param(i, key, false); + } + }; + + std::vector arguments_flat; + for (size_t i = 0; i < params.size(); i++) { + if (!is_value(i)) { + if (params[i].size() > 1 && params[i][1] == '-') { // long -- + add_param(i, 2); + } else { // short - + const size_t j_end = std::min(params[i].size(), params[i].find('=')) - 1; + for (size_t j = 1; j < j_end; j++) { // add possible other flags + const std::string key = std::string(1, params[i][j]); + parse_param(i, key, true); + } + add_param(i, j_end); + } + } else { + arguments_flat.emplace_back(params[i]); + } + } + + // Parse all the positional arguments, making sure multi_argument positional arguments are processed last to enable arguments afterwards + size_t arg_i = 0; + for (; arg_i < arg_entries.size() && !arg_entries[arg_i]->_is_multi_argument; arg_i++) { // iterate over positional arguments until a multi-argument is found + if (arg_i < arguments_flat.size()) + arg_entries[arg_i]->_convert(arguments_flat[arg_i]); + } + size_t arg_j = 1; + for (size_t j_end = arg_entries.size() - arg_i; arg_j <= j_end; arg_j++) { // iterate from back to front, to ensure non-multi-arguments in the front and back are given preference + size_t flat_idx = arguments_flat.size() - arg_j; + if (flat_idx < arguments_flat.size() && flat_idx >= arg_i) { + if (arg_entries[arg_entries.size() - arg_j]->_is_multi_argument) { + std::stringstream s; // Combine multiple arguments into 1 comma-separated string for parsing + copy(&arguments_flat[arg_i],&arguments_flat[flat_idx] + 1, std::ostream_iterator(s,",")); + std::string value = s.str(); + value.back() = '\0'; // remove trailing ',' + arg_entries[arg_i]->_convert(value); + } else { + arg_entries[arg_entries.size() - arg_j]->_convert(arguments_flat[flat_idx]); + } + } + } + + // try to apply default values for arguments which have not been set + for (const auto &entry : all_entries) { + if (!entry->value_.has_value()) { + entry->_apply_default(); + } + } + + if (_help) { + help(); + exit(0); + } + + validate(raise_on_error); + is_valid = true; + } + + void print() const { + for (const auto &entry : all_entries) { + std::string snip = entry->type == Entry::ARG ? "(" + (entry->help.size() > 10 ? entry->help.substr(0, 7) + "..." : entry->help) + ")" : ""; + cout << setw(21) << entry->_get_keys() + snip << " : " << (entry->is_set_by_user? bold(entry->value_.value_or("null")) : entry->value_.value_or("null")) << endl; + } + + for (const auto &[subcommand, subentry] : subcommand_entries) { + if (subentry->subargs->is_valid) { + cout << endl << "--- Subcommand: " << subcommand << endl; + subentry->subargs->print(); + } + } + } + + virtual int run() {return 0;} // For automatically running subcommands + int run_subcommands() { + for (const auto &[subcommand, subentry] : subcommand_entries) { + if (subentry->subargs->is_valid) { + return subentry->subargs->run(); + } + } + + std::cerr << "No subcommand provided" << std::endl; + help(); + return -1; + } + }; + + template T parse(int argc, const char* const *argv, const bool &raise_on_error=false) { + T args = T(); + args.parse(argc, argv, raise_on_error); + return args; + } +} diff --git a/main.cpp b/main.cpp index a7637bd..e4b06d2 100644 --- a/main.cpp +++ b/main.cpp @@ -9,6 +9,8 @@ // folder). // - Introduction, links and more at the top of imgui.cpp +#include +#include #include #include "lib/backends/imgui_impl_opengl3.h" @@ -16,6 +18,7 @@ #include "lib/imgui.h" #include "lib/imgui_internal.h" #include +#include #include #define STB_IMAGE_IMPLEMENTATION @@ -39,6 +42,11 @@ #include "lib/imgui_tex_inspect.h" #include "lib/backends/tex_inspect_opengl.h" +#include "lib/argparse.hpp" + +struct Args : public argparse::Args { + std::string &fpath = arg("path to the image"); +}; struct Texture { @@ -77,9 +85,12 @@ Texture LoadTexture(const char * path) static bool init = true; +static bool showHelp = false; +static int mode = 0; +const int maxAnnotatedTexels = 10000; // Main code -int main(int, char **) { +int main(int argc, char* argv[]) { // Setup SDL if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_GAMECONTROLLER) != 0) { @@ -87,6 +98,10 @@ int main(int, char **) { return -1; } + bool TOOLTIP_ENABLED = false; + bool GRID_ENABLED = true; + bool AA_ENABLED = true; + // Decide GL+GLSL versions #if defined(IMGUI_IMPL_OPENGL_ES2) // GL ES 2.0 + GLSL 100 @@ -160,32 +175,66 @@ int main(int, char **) { Texture t; + if (init) { + init = false; + try { + auto args = argparse::parse(argc, argv, true); + t = LoadTexture(args.fpath.c_str()); + } catch (const std::runtime_error &e) { + std::cerr << "failed to parse arguments: " << e.what() << std::endl; + return -1; + } + } + // Main loop bool done = false; while (!done) { + auto flags = ImGuiTexInspect::InspectorFlags_FillVertical | ImGuiTexInspect::InspectorFlags_FillHorizontal; + // Poll and handle events (inputs, window resize, etc.) SDL_Event event; while (SDL_PollEvent(&event)) { ImGui_ImplSDL2_ProcessEvent(&event); - if (event.type == SDL_QUIT) - done = true; if (event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_CLOSE && event.window.windowID == SDL_GetWindowID(window)) done = true; + + switch (event.type) { + case SDL_QUIT: + done = true; + break; + case SDL_KEYDOWN: + switch (event.key.keysym.scancode) { + case SDL_SCANCODE_G: + GRID_ENABLED = !GRID_ENABLED; + break; + case SDL_SCANCODE_T: + TOOLTIP_ENABLED = !TOOLTIP_ENABLED; + break; + case SDL_SCANCODE_A: + AA_ENABLED = !AA_ENABLED; + break; + case SDL_SCANCODE_H: + showHelp = !showHelp; + break; + case SDL_SCANCODE_D: + mode = (mode + 1) % 5; + break; + case SDL_SCANCODE_Q: + done = true; + break; + default: + break; + } + } } // Start the Dear ImGui frame ImGui_ImplOpenGL3_NewFrame(); ImGui_ImplSDL2_NewFrame(); ImGui::NewFrame(); - - if (init) { - init = false; - t = LoadTexture("/home/dubey/Pictures/DSC03199.JPG"); - } - { #ifdef IMGUI_HAS_VIEWPORT ImGuiViewport *viewport = ImGui::GetMainViewport(); @@ -206,18 +255,78 @@ int main(int, char **) { ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoResize); auto wSize = ImGui::GetContentRegionAvail(); if (t.texture != 0) { + + if (!TOOLTIP_ENABLED) { + flags = flags | ImGuiTexInspect::InspectorFlags_NoTooltip; + } + ImGuiTexInspect::BeginInspectorPanel( "Inspector", t.texture, t.size, - ImGuiTexInspect::InspectorFlags_FillVertical | ImGuiTexInspect::InspectorFlags_FillHorizontal, + flags, ImGuiTexInspect::SizeExcludingBorder(wSize) ); + + if (GRID_ENABLED) { + CurrentInspector_ClearFlags(ImGuiTexInspect::InspectorFlags_NoGrid); + }else { + CurrentInspector_SetFlags(ImGuiTexInspect::InspectorFlags_NoGrid); + } + + if (TOOLTIP_ENABLED) { + CurrentInspector_ClearFlags(ImGuiTexInspect::InspectorFlags_NoTooltip); + }else { + CurrentInspector_SetFlags(ImGuiTexInspect::InspectorFlags_NoTooltip); + } + + if (AA_ENABLED) { + CurrentInspector_ClearFlags(ImGuiTexInspect::InspectorFlags_NoForceFilterNearest); + }else { + CurrentInspector_SetFlags(ImGuiTexInspect::InspectorFlags_NoForceFilterNearest); + } + + switch(mode) { + case 1: + ImGuiTexInspect::DrawAnnotations(ImGuiTexInspect::Arrow().UsePreset(ImGuiTexInspect::Arrow::NormalMap), maxAnnotatedTexels); + break; + case 2: + ImGuiTexInspect::DrawAnnotations(ImGuiTexInspect::ValueText(ImGuiTexInspect::ValueText::Format::HexString), maxAnnotatedTexels); + break; + case 3: + ImGuiTexInspect::DrawAnnotations(ImGuiTexInspect::ValueText(ImGuiTexInspect::ValueText::Format::BytesDec), maxAnnotatedTexels); + break; + case 4: + ImGuiTexInspect::DrawAnnotations(ImGuiTexInspect::ValueText(ImGuiTexInspect::ValueText::Format::Floats), maxAnnotatedTexels); + break; + default: + break; + } + + ImGuiTexInspect::EndInspectorPanel(); } ImGui::End(); ImGui::PopStyleVar(); ImGui::PopStyleVar(); + + if (showHelp){ + ImGui::OpenPopup("HelpPopup"); + showHelp = !showHelp; + } + if (ImGui::BeginPopup("HelpPopup")) { + ImGui::Text("tview Help"); + ImGui::Separator(); + ImGui::Text("g - toggle grid"); + ImGui::Text("a - toggle filtering"); + ImGui::Text("t - toggle tooltip"); + ImGui::Text("d - cycle pixel detail mode"); + ImGui::Text("q - quit"); + ImGui::Text("h - show help popup"); + ImGui::Separator(); + ImGui::Text("click anywhere to continue"); + ImGui::EndPopup(); + } } // Rendering