diff --git a/CMakeLists.txt b/CMakeLists.txt index aa5f367..4a243cf 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,11 +19,13 @@ message(STATUS "GLFW:\t" ${glfw3_module_DIR}) message(STATUS "PFD:\t\t" ${pfd_DIR}) message(STATUS "SPDLOG:\t" ${spdlog_DIR}) -if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - set(CMAKE_CXX_FLAGS "/std:c++20 /Zi /EHsc") -endif() -if (WIN32 AND NOT CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") - set(CMAKE_CXX_FLAGS "-Wall -Wextra -g --std=c++20") +if(WIN32) + set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") + if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") + set(CMAKE_CXX_FLAGS "/std:c++20 /Zi /EHsc") + else() + set(CMAKE_CXX_FLAGS "-Wall -Wextra -g --std=c++20") + endif() endif() if(UNIX) set(CMAKE_CXX_FLAGS "-Wall -Wextra -g --std=c++20") @@ -40,8 +42,6 @@ add_subdirectory(lib) set(SRC src/MyApp.cpp - src/views/PixelariumImageView.cpp - src/viewmodels/ImageViewFactory.cpp src/main.cpp) #==================== diff --git a/CMakePresets.json b/CMakePresets.json index 6fc526f..579329f 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -8,6 +8,7 @@ "configurePresets": [ { "name": "default", + "hidden": true, "generator": "Ninja", "binaryDir": "build", "cacheVariables": { @@ -20,12 +21,14 @@ { "name": "clang-release", "inherits": "default", + "binaryDir": "build/clang-release", "cacheVariables": { "CMAKE_BUILD_TYPE": "Release" } }, { "name": "clang-debug", + "binaryDir": "build/clang-debug", "inherits": "default", "cacheVariables": { "CMAKE_BUILD_TYPE": "Debug" @@ -34,6 +37,7 @@ { "name": "gcc-release", "inherits": "default", + "binaryDir": "build/gcc-release", "cacheVariables": { "CMAKE_C_COMPILER": "gcc", "CMAKE_CXX_COMPILER": "g++", @@ -43,6 +47,7 @@ { "name": "gcc-debug", "inherits": "default", + "binaryDir": "build/gcc-debug", "cacheVariables": { "CMAKE_C_COMPILER": "gcc", "CMAKE_CXX_COMPILER": "g++", @@ -53,7 +58,8 @@ "buildPresets": [ { "name": "default", - "configurePreset": "default", + "hidden": true, + "configurePreset": "clang-debug", "jobs": 10 }, { diff --git a/cmake/libCZI.cmake b/cmake/libCZI.cmake index 01ef7aa..66233d9 100644 --- a/cmake/libCZI.cmake +++ b/cmake/libCZI.cmake @@ -3,7 +3,7 @@ include(FetchContent) FetchContent_Declare( libCZI GIT_REPOSITORY https://github.com/ZEISS/libczi.git - GIT_TAG origin/main + GIT_TAG 593ee17587214358c535bd036473b1b62945e637 ) if(NOT libCZI_POPULATED) diff --git a/lib/app/AppGLFW.cpp b/lib/app/AppGLFW.cpp index 67b7ca6..9a01a9f 100644 --- a/lib/app/AppGLFW.cpp +++ b/lib/app/AppGLFW.cpp @@ -1,15 +1,29 @@ #include "AppGLFW.hpp" +#include "app_resources_default.h" #include "imgui.h" #include "imgui_impl_glfw.h" #include "imgui_impl_opengl3.h" -#include "app_resources_default.h" +/// @brief GLFW error callback function. +/// @param error The error code. +/// @param description A description of the error. static void glfw_error_callback(int error, const char* description) { fprintf(stderr, "GLFW Error %d: %s\n", error, description); } +/// @brief Initializes the main application window using GLFW. +/// +/// This function sets up the GLFW error callback, initializes GLFW, +/// selects appropriate OpenGL and GLSL versions based on platform, +/// creates the main application window, sets up the OpenGL context, +/// enables vsync, and initializes the Dear ImGui context, style, and +/// platform/renderer backends. It uses the primary monitor's workarea +/// to determine the screen resolution but creates a window of a fixed size (1200x800). +/// +/// @return void. The function returns void and uses error handling internally to manage GLFW and window creation +/// failures. The application may continue to run in a failed state, but the window will not be initialized. void pixelarium::application::AppGLFW::InitMainWindow() { glfwSetErrorCallback(glfw_error_callback); @@ -95,6 +109,8 @@ void pixelarium::application::AppGLFW::InitMainWindow() ImGui_ImplOpenGL3_Init(glsl_version); } +/// @brief Main application loop. This function handles window events, rendering, and ImGui integration. +/// @return 0 if the application closes successfully. int pixelarium::application::AppGLFW::RunLoop() { ImGuiIO& io = ImGui::GetIO(); @@ -141,6 +157,10 @@ int pixelarium::application::AppGLFW::RunLoop() return 0; } +/// @brief Creates and displays the main menu bar. +/// +/// This function constructs the application's main menu bar using ImGui. It includes a log level selector +/// and calls other functions to populate additional menu bar sections. void pixelarium::application::AppGLFW::MenuBar() { if (ImGui::BeginMainMenuBar()) @@ -179,6 +199,7 @@ void pixelarium::application::AppGLFW::MenuBar() } } +/// @brief Allows the user to select the log level via a combo box. void pixelarium::application::AppGLFW::LogLevelSelect() { if (ImGui::BeginCombo(LOGLEVELSELECT, LOGLEVELS[log_level_].data())) diff --git a/lib/imaging/PixelariumImage.cpp b/lib/imaging/PixelariumImage.cpp index 332a19e..1937d92 100644 --- a/lib/imaging/PixelariumImage.cpp +++ b/lib/imaging/PixelariumImage.cpp @@ -13,5 +13,6 @@ pixelarium::imaging::PixelariumImage::PixelariumImage(const std::string& uri) throw std::runtime_error(std::format("File not {} found", uri)); } + this->uri_ = std::filesystem::path(uri); this->img_ = std::make_unique(cv::imread(uri)); } diff --git a/lib/imaging/PixelariumImage.hpp b/lib/imaging/PixelariumImage.hpp index 2abad19..38263b3 100644 --- a/lib/imaging/PixelariumImage.hpp +++ b/lib/imaging/PixelariumImage.hpp @@ -1,45 +1,85 @@ #pragma once +#include +#include #include #include #include namespace pixelarium::imaging { +using AccessorFunctor = std::function; + +enum class ImageFileType +{ + ABSRACT = 0, + PNG = 1, + JPG = 2, + CZI = 3, +}; +/// @brief This aims to be a generic image abstraction +/// meant for codec specific implementation. + +/// Todo: the above implies that most of the below implementations don't make sense for this class (c.f. cv::Mat +/// generation et.al.) class PixelariumImage { public: + // get back the defaults + // this means, that there has to be and API option to set + // a resource which should trigger some sort of action + // after setting explicit PixelariumImage(const std::string& uri); - // get back the defaults PixelariumImage() = default; PixelariumImage(const PixelariumImage& other) { // be ware!! // we make a copy of the image data here! img_ = std::make_unique(*other.img_); + uri_ = other.uri_; }; - PixelariumImage(PixelariumImage&& other) noexcept - : img_(std::move(other.img_)) {} - // requires a copy ctor which we don't have + PixelariumImage(PixelariumImage&& other) noexcept : img_(std::move(other.img_)) {} PixelariumImage& operator=(const PixelariumImage& other) = delete; PixelariumImage& operator=(PixelariumImage&& other) noexcept { if (this != &other) { img_ = std::move(other.img_); + uri_ = other.uri_; } return *this; } + // this should probably vanish as it makes no sense + // for multidimensional images (more than one frame) + // -> we need some sort of accessor functionality ~PixelariumImage() = default; const cv::Mat& GetImage() const { return *this->img_.get(); } + const std::string Name() const noexcept + { + if (!this->uri_.empty()) + { + return this->uri_.filename().string(); + } + + return {}; + } + + bool Empty() const noexcept { return this->img_->empty(); } + + static ImageFileType Type() { return PixelariumImage::type_; } + protected: std::unique_ptr img_; + + std::filesystem::path uri_; + + static ImageFileType type_; }; } // namespace pixelarium::imaging diff --git a/lib/rendering/CMakeLists.txt b/lib/rendering/CMakeLists.txt index ccb0063..1bf12fd 100644 --- a/lib/rendering/CMakeLists.txt +++ b/lib/rendering/CMakeLists.txt @@ -1,7 +1,10 @@ set(RENDERLIBNAME pixelariumrenderlib) set(RENDERLIBSRC - CvMatRender.cpp) + CvMatRender.cpp + RenderImageManager.cpp + PixelariumImageView.cpp + ImageViewFactory.cpp) add_library(${RENDERLIBNAME} STATIC ${RENDERLIBSRC}) @@ -10,4 +13,5 @@ target_link_libraries(${RENDERLIBNAME} PRIVATE pixelariumimagelib) target_include_directories(${RENDERLIBNAME} - PRIVATE ${CMAKE_SOURCE_DIR}/lib) \ No newline at end of file + PRIVATE ${CMAKE_SOURCE_DIR}/lib + PRIVATE ${imgui_DIR}) diff --git a/lib/rendering/ImageViewFactory.cpp b/lib/rendering/ImageViewFactory.cpp new file mode 100644 index 0000000..4c457d4 --- /dev/null +++ b/lib/rendering/ImageViewFactory.cpp @@ -0,0 +1,22 @@ +#include "ImageViewFactory.hpp" + +#include +#include + +/// @brief Creates a PixelariumImageView from a resource image. +/// @param image_id The ID of the image resource to render. +/// @return A unique pointer to the PixelariumImageView, or nullptr if the image resource is not found or is empty. The image data is copied. +std::unique_ptr pixelarium::render::ImageViewFactory::RenderImage( + size_t image_id) +{ + auto img{this->image_pool_.GetResource(image_id)}; + + if (!img.has_value() || img.value()->Empty()) + { + return nullptr; + } + + // beware: here we copy the actual image resource over to the new image + return std::make_unique(std::make_shared(*img.value())); +} + diff --git a/src/viewmodels/ImageViewFactory.hpp b/lib/rendering/ImageViewFactory.hpp similarity index 52% rename from src/viewmodels/ImageViewFactory.hpp rename to lib/rendering/ImageViewFactory.hpp index bfd6018..2346a24 100644 --- a/src/viewmodels/ImageViewFactory.hpp +++ b/lib/rendering/ImageViewFactory.hpp @@ -1,9 +1,9 @@ #pragma once -#include "PixelariumImage.hpp" +#include "PixelariumImageView.hpp" +#include "imaging/PixelariumImage.hpp" #include "resources/resource.hpp" -#include "views/PixelariumImageView.hpp" -namespace pixelarium::ui +namespace pixelarium::render { class ImageViewFactory { @@ -13,15 +13,9 @@ class ImageViewFactory public: explicit ImageViewFactory(Pool& pool) : image_pool_(pool) {} - [[nodiscard("Image Id is ignored")]] - size_t AddImage(std::unique_ptr res) const noexcept - { - return image_pool_.SetResource(std::move(res)); - } - std::unique_ptr RenderImage(size_t id); private: Pool& image_pool_; }; -} // namespace pixelarium::ui +} // namespace pixelarium::render diff --git a/lib/rendering/PixelariumImageView.cpp b/lib/rendering/PixelariumImageView.cpp new file mode 100644 index 0000000..355620e --- /dev/null +++ b/lib/rendering/PixelariumImageView.cpp @@ -0,0 +1,70 @@ +#include "PixelariumImageView.hpp" + +#include + +#include "imgui.h" + +/// @brief Checks if the dimensions of two ImVec2 vectors have changed significantly. +/// @param ref_rect The reference ImVec2 vector. +/// @param new_rect The new ImVec2 vector to compare against. +/// @return True if the absolute difference between the y-coordinates is greater than 5 or the x-coordinates are +/// different; otherwise, false. +static bool dim_changed_p(const ImVec2& ref_rect, const ImVec2& new_rect) +{ + if (std::abs(ref_rect.y - new_rect.y) > 5 || std::abs(ref_rect.x - new_rect.x)) + { + return true; + } + + return false; +} + +/// @brief Calculates dimensions to maintain aspect ratio. +/// @param img The input image. +/// @param curr_dim The current dimensions. +/// @return The calculated dimensions maintaining aspect ratio. +ImVec2 aspect_const_dimensions(const pixelarium::imaging::PixelariumImage& img, const ImVec2& curr_dim) +{ + const auto w_fact = (static_cast(curr_dim.x) / img.GetImage().cols); + const auto h_fact = (static_cast(curr_dim.y) / img.GetImage().rows); + + const auto fact = w_fact < h_fact ? w_fact : h_fact; + + return ImVec2(img.GetImage().cols * fact, img.GetImage().rows * fact); +} + +/// @brief Displays the image in an ImGui window. +/// +/// If the image is null, empty, or has an empty name, the function returns immediately. Otherwise, it creates an ImGui +/// window with a horizontal scrollbar and menu bar. The image is rendered using the CvMatRender object, resizing it to +/// fit the available window space. The raw and rendered dimensions are displayed below the image. +void pixelarium::render::PixelariumImageView::ShowImage() +{ + if (this->img_ == nullptr || this->img_->Empty() || this->img_->Name().empty()) + { + // do nothing + return; + } + + ImGui::Begin(img_->Name().c_str(), &this->open_p, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_MenuBar); + + this->curr_dim_ = ImGui::GetContentRegionAvail(); + auto new_dim = ImGui::GetContentRegionAvail(); + auto texture = + dim_changed_p(this->curr_dim_, new_dim) + ? this->render_.Render(static_cast(this->curr_dim_.x), static_cast(this->curr_dim_.y)) + : this->render_.Render(); + + this->curr_dim_ = new_dim; + + ImVec2 dim(this->img_->GetImage().cols, this->img_->GetImage().rows); + + ImGui::Image(reinterpret_cast(reinterpret_cast(texture)), + aspect_const_dimensions(*this->img_, new_dim)); + + ImGui::Separator(); + ImGui::Text("%s", std::format(" Raw Dimensions W : {}, H: {}", dim.x, dim.y).c_str()); + ImGui::Text("%s", std::format("Render Dimensions W : {}, H: {}", curr_dim_.x, curr_dim_.y).c_str()); + + ImGui::End(); +} diff --git a/src/views/PixelariumImageView.hpp b/lib/rendering/PixelariumImageView.hpp similarity index 75% rename from src/views/PixelariumImageView.hpp rename to lib/rendering/PixelariumImageView.hpp index 0f6a149..f6c7355 100644 --- a/src/views/PixelariumImageView.hpp +++ b/lib/rendering/PixelariumImageView.hpp @@ -2,11 +2,11 @@ #include -#include "PixelariumImage.hpp" +#include "imaging/PixelariumImage.hpp" #include "imgui.h" #include "rendering/CvMatRender.hpp" -namespace pixelarium::ui +namespace pixelarium::render { class PixelariumImageView { @@ -22,13 +22,14 @@ class PixelariumImageView PixelariumImageView& operator=(PixelariumImageView&) = delete; PixelariumImageView& operator=(PixelariumImageView&&) = delete; - void ToggleView(bool target) { open_p = target; } + // void ToggleView(bool target) { open_p = target; } + const bool* GetStatus() const noexcept { return &this->open_p; } void ShowImage(); private: const std::shared_ptr img_; Render render_; - bool open_p{false}; + bool open_p{true}; ImVec2 curr_dim_{}; }; -} // namespace pixelarium::ui +} // namespace pixelarium::render diff --git a/lib/rendering/RenderImageManager.cpp b/lib/rendering/RenderImageManager.cpp new file mode 100644 index 0000000..dc620aa --- /dev/null +++ b/lib/rendering/RenderImageManager.cpp @@ -0,0 +1,76 @@ +#include "RenderImageManager.hpp" + +#include + +using namespace std; + +/// @brief Updates the collection of rendered images by removing images marked for deletion. +/// This function iterates through the \c keys_to_delete_ list and removes the corresponding images from the collection. +/// It does not acquire the mutex to avoid deadlocks with the `Remove` function. +void pixelarium::render::RenderImageManager::UpdateCollection() +{ + for (const auto& key : keys_to_delete_) + { + this->Remove(key); + } + + keys_to_delete_.clear(); +} + +/// @brief Marks a resource for deletion. +/// @param key The ID of the resource to mark for deletion. +void pixelarium::render::RenderImageManager::MarkForDeletion(resources::ResourceKey key) +{ + this->log_.Debug(std::format("{} marking key: \"{}\" for deletion.", __PRETTY_FUNCTION__, key)); + lock_guard guard(this->mut_); + keys_to_delete_.insert(key); +} + +/// @brief Removes a render image from the manager. +/// @param key The key of the render image to remove. +/// @return True if the render image was removed, false otherwise. +bool pixelarium::render::RenderImageManager::Remove(resources::ResourceKey key) noexcept +{ + bool remove_state{false}; + this->log_.Debug(std::format("{} removing key: \"{}\" from renderlist.", __PRETTY_FUNCTION__, key)); + { + lock_guard guard(this->mut_); + remove_state = this->render_image_map_.erase(key) == 1; + } + + return remove_state; +} + +/// @brief Adds a resource to the render image map. +/// @param key The ID of the resource to add. +/// @return void. No exception is thrown. +void pixelarium::render::RenderImageManager::Add(resources::ResourceKey key) noexcept +{ + // we don't want to add what's already there + // or empty render images + auto current_view = this->view_factory_->RenderImage(key); + if (this->render_image_map_.contains(key) || current_view == nullptr) + { + return; + } + + this->log_.Debug(std::format("{}: adding key: \"{}\" to renderlist.", __PRETTY_FUNCTION__, key)); + + lock_guard guard(this->mut_); + // clang-format off + this->render_image_map_.insert( + { + key, + RenderImageStateWrapper + { + .view{ std::move(current_view) }, + .show_state = current_view->GetStatus(), + } + } + ); + // clang-format on +} + +/// @brief Clears all render images from the manager. +/// @note This function is noexcept. +void pixelarium::render::RenderImageManager::Clear() noexcept { this->render_image_map_.clear(); } diff --git a/lib/rendering/RenderImageManager.hpp b/lib/rendering/RenderImageManager.hpp new file mode 100644 index 0000000..d1059e2 --- /dev/null +++ b/lib/rendering/RenderImageManager.hpp @@ -0,0 +1,64 @@ +#pragma once +#include +#include +#include +#include + +#include "ImageViewFactory.hpp" +#include "PixelariumImageView.hpp" +#include "resources/resource.hpp" +#include "utilities/ILog.hpp" + +// This is intended as an additional abstraction +// aggregating views that should be rendered (or not) +namespace pixelarium::render +{ +struct RenderImageStateWrapper +{ + std::unique_ptr view; + const bool* show_state; +}; + +class RenderImageManager +{ + using Pool = resources::ImageResourcePool; + + public: + explicit RenderImageManager(Pool& pool, const utils::log::ILog& log) + : view_factory_(std::make_unique(pool)), log_(log) + { + } + + void Clear() noexcept; + + void Add(resources::ResourceKey key) noexcept; + + bool Remove(resources::ResourceKey key) noexcept; + + // can't be const because func mutates the state + template + requires std::invocable + void Enumerate(Callable&& func) + { + for (auto& [key, val] : this->render_image_map_) + { + if (val.view != nullptr) + { + func(key, val); + } + } + } + + void MarkForDeletion(resources::ResourceKey key); + + void UpdateCollection(); + + private: + std::unordered_map render_image_map_; + std::unique_ptr view_factory_; + std::mutex mut_; + std::unordered_set keys_to_delete_; + + const utils::log::ILog& log_; +}; +} // namespace pixelarium::render diff --git a/lib/resources/resource.cpp b/lib/resources/resource.cpp index d98e10b..b4b9090 100644 --- a/lib/resources/resource.cpp +++ b/lib/resources/resource.cpp @@ -1,7 +1,9 @@ #include "resource.hpp" #include +#include #include +#include #include using pixelarium::imaging::PixelariumImage; @@ -20,7 +22,7 @@ size_t GenerateId() { return id_.fetch_add(1, memory_order_relaxed); } /// @brief Retrieves a resource from the pool. /// @param id The ID of the resource to retrieve. /// @return A pointer to the resource if found, otherwise an empty optional. -std::optional pixelarium::resources::ImageResourcePool::GetResource(size_t id) const +std::optional pixelarium::resources::ImageResourcePool::GetResource(ResourceKey id) const { auto search{this->resources_.find(id)}; if (search == this->resources_.end()) return std::nullopt; @@ -34,7 +36,10 @@ std::optional pixelarium::resources::ImageResourcePool:: size_t pixelarium::resources::ImageResourcePool::SetResource(unique_ptr res) { auto key{::GenerateId()}; - this->resources_.insert({key, std::move(res)}); + { + std::lock_guard guard(this->mut_); + this->resources_.insert({key, std::move(res)}); + } return key; } @@ -43,7 +48,8 @@ size_t pixelarium::resources::ImageResourcePool::SetResource(unique_ptr res) +bool pixelarium::resources::ImageResourcePool::ModifyResource(ResourceKey id, + std::unique_ptr res) { auto search{this->resources_.find(id)}; if (search == this->resources_.end()) return false; @@ -56,7 +62,7 @@ bool pixelarium::resources::ImageResourcePool::ModifyResource(size_t id, std::un /// @brief Deletes a resource from the pool. /// @param id The ID of the resource to delete. /// @return True if the resource was deleted, false otherwise. -bool pixelarium::resources::ImageResourcePool::DeleteResource(size_t id) +bool pixelarium::resources::ImageResourcePool::DeleteResource(ResourceKey id) { auto search{this->resources_.find(id)}; if (search == this->resources_.end()) return false; @@ -70,10 +76,12 @@ bool pixelarium::resources::ImageResourcePool::DeleteResource(size_t id) /// @param func A function to call for each resource. The function should accept the resource ID and a const reference /// to a PixelariumImage. void pixelarium::resources::ImageResourcePool::EnumerateResources( - const std::function& func) + const std::function& func) { + size_t idx{0}; for (const auto& e : this->resources_) { - func(e.first, *e.second); + func(e.first, idx, *e.second); + ++idx; } } diff --git a/lib/resources/resource.hpp b/lib/resources/resource.hpp index 5d3a4a8..b7478f0 100644 --- a/lib/resources/resource.hpp +++ b/lib/resources/resource.hpp @@ -3,6 +3,7 @@ #include #include #include +#include #include #include @@ -10,6 +11,7 @@ namespace pixelarium::resources { + using ResourceKey = size_t; struct IResource { virtual ~IResource() = default; @@ -24,10 +26,10 @@ class IResourcePool public: virtual ~IResourcePool() = default; virtual std::optional GetResource(size_t id) const = 0; - virtual size_t SetResource(std::unique_ptr res) = 0; - virtual bool ModifyResource(size_t id, std::unique_ptr res) = 0; - virtual bool DeleteResource(size_t id) = 0; - virtual void EnumerateResources(const std::function& func) = 0; + virtual ResourceKey SetResource(std::unique_ptr res) = 0; + virtual bool ModifyResource(ResourceKey id, std::unique_ptr res) = 0; + virtual bool DeleteResource(ResourceKey id) = 0; + virtual void EnumerateResources(const std::function& func) = 0; virtual size_t GetTotalSize() const = 0; }; @@ -46,20 +48,21 @@ class ImageResourcePool : public IResourcePool ImageResourcePool& operator=(ImageResourcePool&) = delete; ImageResourcePool& operator=(ImageResourcePool&&) = delete; - std::optional GetResource(size_t id) const override; - size_t SetResource(std::unique_ptr res) override; - bool ModifyResource(size_t id, std::unique_ptr res) override; - bool DeleteResource(size_t id) override; + std::optional GetResource(ResourceKey id) const override; + ResourceKey SetResource(std::unique_ptr res) override; + bool ModifyResource(ResourceKey id, std::unique_ptr res) override; + bool DeleteResource(ResourceKey id) override; - void EnumerateResources(const std::function& func) override; + void EnumerateResources(const std::function& func) override; template - requires std::invocable + requires std::invocable void Enumerate(Callable&& func) const { + size_t idx{0}; for (const auto& e : this->resources_) { - func(e.first, *e.second); + func(e.first, idx, *e.second); } } @@ -67,5 +70,6 @@ class ImageResourcePool : public IResourcePool private: std::unordered_map> resources_; + std::mutex mut_; }; } // namespace pixelarium::resources diff --git a/src/MyApp.cpp b/src/MyApp.cpp index 8d1c1d2..41805fe 100644 --- a/src/MyApp.cpp +++ b/src/MyApp.cpp @@ -1,13 +1,15 @@ #include "MyApp.hpp" +#include #include #include +#include "app_resources_default.h" +#include "app_resources_local.h" #include "imaging/PixelariumImage.hpp" #include "imgui.h" #include "portable-file-dialogs.h" -#include "app_resources_default.h" -#include "app_resources_local.h" +#include "rendering/RenderImageManager.hpp" #include "utilities/ILog.hpp" using namespace pixelarium::imaging; @@ -35,17 +37,68 @@ void pixelarium::ui::MyApp::Run() { if (demop_) ImGui::ShowDemoWindow(&this->demop_); if (image_listp_) this->ImageGalleryRender(); + + this->RenderImages(); +} + +void pixelarium::ui::MyApp::RenderImages() +{ + this->render_manager_->Enumerate( + [&](resources::ResourceKey key, render::RenderImageStateWrapper& render_state) + { + render_state.view->ShowImage(); + + if (!*render_state.view->GetStatus()) + { + this->render_manager_->MarkForDeletion(key); + } + }); } void pixelarium::ui::MyApp::ImageGalleryRender() { - if (ImGui::BeginListBox("ListBox")) + ImGui::SetNextWindowSize(ImVec2(300, 500)); + ImGui::Begin(SHOWIMAGEGALLERY); + + // this updates the render collection + // essentially deleting render views that were + // marked for deletion + this->render_manager_->UpdateCollection(); + + static size_t selected_index{0}; + int highlight_index{-1}; + + if (ImGui::BeginListBox("Image List", ImVec2(200, 400))) { - pool_.EnumerateResources([](size_t id, const imaging::PixelariumImage&) -> void - { ImGui::Selectable(std::format("Image {}", id).c_str()); }); + pool_.EnumerateResources( + [&](size_t id, size_t idx, const imaging::PixelariumImage& img) -> void + { + const bool is_selected = selected_index == idx; + if (ImGui::Selectable(std::format("{}", img.Name()).c_str(), is_selected)) + { + selected_index = idx; + this->selected_image_ = id; + } + if (highlight_index && ImGui::IsItemHovered()) highlight_index = idx; + + if (is_selected) ImGui::SetItemDefaultFocus(); + }); ImGui::EndListBox(); } + + ImGui::Checkbox(AUTOSHOWSELECTED, &this->auto_show_selectd_image_); + ImGui::SameLine(); // put the button next to the checkbox + // note that the button will only show when the checkbox is toggled off + // this is intended behavior as the selected image will render automatically + // when the checkbox is toggled on + if (this->auto_show_selectd_image_ || ImGui::Button(OPENIMAGE)) + { + // Try add the selected index to the manager + this->render_manager_->Add(this->selected_image_); + } + + ImGui::End(); // end gallery window } void pixelarium::ui::MyApp::LoadImageProt() @@ -56,6 +109,6 @@ void pixelarium::ui::MyApp::LoadImageProt() { this->logger_.Debug(std::format("{}: Creating image {}", __FUNCTION__, p)); - last_id = image_view_model_->AddImage(std::make_unique(p)); + pool_.SetResource(std::make_unique(p)); } } diff --git a/src/MyApp.hpp b/src/MyApp.hpp index 60fc038..4a694ef 100644 --- a/src/MyApp.hpp +++ b/src/MyApp.hpp @@ -1,12 +1,13 @@ #pragma once +#include #include #include "AppGLFW.hpp" #include "imgui.h" +#include "rendering/RenderImageManager.hpp" #include "resources/resource.hpp" #include "utilities/ILog.hpp" -#include "viewmodels/ImageViewFactory.hpp" namespace pixelarium::ui { @@ -14,9 +15,10 @@ class MyApp : public application::AppGLFW { public: MyApp(const utils::log::ILog& log, pixelarium::resources::ImageResourcePool& pool) - : application::AppGLFW(log), pool_(pool) + : application::AppGLFW(log), + pool_(pool), + render_manager_(std::make_unique(pool, log)) { - image_view_model_ = std::make_unique(pool_); } protected: @@ -27,12 +29,15 @@ class MyApp : public application::AppGLFW private: void LoadImageProt(); void ImageGalleryRender(); + void RenderImages(); private: resources::ImageResourcePool& pool_; - std::unique_ptr image_view_model_; - bool image_listp_{false}; + std::unique_ptr render_manager_; + bool image_listp_{true}; + bool auto_show_selectd_image_{true}; bool demop_{false}; ImVec2 curr_dim_; + size_t selected_image_{0}; }; } // namespace pixelarium::ui diff --git a/src/app_resources_local.h.in b/src/app_resources_local.h.in index 1fb160c..e47a7cb 100644 --- a/src/app_resources_local.h.in +++ b/src/app_resources_local.h.in @@ -3,6 +3,10 @@ // Currently, there is no need for this to be a configurable header. // It is left as such to keep the API open for possible build-system // injections in future. +// clang-format off +#define SHOWIMGUIDEMOS "ImGui Demos" +#define SHOWIMAGEGALLERY "Image Gallery" +#define AUTOSHOWSELECTED "Auto View" +#define OPENIMAGE "Open" -#define SHOWIMGUIDEMOS "ImGui Demos" -#define SHOWIMAGEGALLERY "Image Gallery" +// clang-format on diff --git a/src/viewmodels/ImageViewFactory.cpp b/src/viewmodels/ImageViewFactory.cpp deleted file mode 100644 index 633565b..0000000 --- a/src/viewmodels/ImageViewFactory.cpp +++ /dev/null @@ -1,15 +0,0 @@ -#include "ImageViewFactory.hpp" -#include -#include - -using namespace pixelarium::ui; - -std::unique_ptr ImageViewFactory::RenderImage(size_t image_id) -{ - auto img{this->image_pool_.GetResource(image_id)}; - - if (!img.has_value()) return nullptr; - - // beware: here we copy the actual image resource over to the new image - return std::make_unique(std::make_shared(*img.value())); -} diff --git a/src/views/PixelariumImageView.cpp b/src/views/PixelariumImageView.cpp deleted file mode 100644 index 069846f..0000000 --- a/src/views/PixelariumImageView.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "PixelariumImageView.hpp" - -#include - -#include "imgui.h" - -using namespace pixelarium::ui; - -static bool dim_changed_p(const ImVec2& ref_rect, const ImVec2& new_rect) -{ - if (std::abs(ref_rect.y - new_rect.y) > 5 || std::abs(ref_rect.x - new_rect.x)) - { - return true; - } - - return false; -} - -ImVec2 aspect_const_dimensions(const pixelarium::imaging::PixelariumImage& img, const ImVec2& curr_dim) -{ - const auto w_fact = (static_cast(curr_dim.x) / img.GetImage().cols); - const auto h_fact = (static_cast(curr_dim.y) / img.GetImage().rows); - - const auto fact = w_fact < h_fact ? w_fact : h_fact; - - return ImVec2(img.GetImage().cols * fact, img.GetImage().rows * fact); -} - -void PixelariumImageView::ShowImage() -{ - if (this->open_p) - { - ImGui::Begin("An image", &this->open_p, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_MenuBar); - this->curr_dim_ = ImGui::GetContentRegionAvail(); - auto new_dim = ImGui::GetContentRegionAvail(); - auto texture = - dim_changed_p(this->curr_dim_, new_dim) - ? this->render_.Render(static_cast(this->curr_dim_.x), static_cast(this->curr_dim_.y)) - : this->render_.Render(); - - this->curr_dim_ = new_dim; - - ImVec2 dim(this->img_->GetImage().cols, this->img_->GetImage().rows); - - ImGui::Image(reinterpret_cast(reinterpret_cast(texture)), - aspect_const_dimensions(*this->img_, new_dim)); - - ImGui::Separator(); - ImGui::Text("%s", std::format("Dimensions W: {}, H: {}", curr_dim_.x, curr_dim_.y).c_str()); - - ImGui::End(); - } -} diff --git a/tests/lib/resources/test_resource.cpp b/tests/lib/resources/test_resource.cpp index 4cbf38d..10a784c 100644 --- a/tests/lib/resources/test_resource.cpp +++ b/tests/lib/resources/test_resource.cpp @@ -82,7 +82,7 @@ TEST(ImageResourcePoolTest, EnumerateResources) auto id2 = pool.SetResource(std::make_unique()); std::vector found_ids{}; - pool.EnumerateResources([&found_ids](size_t id, const pixelarium::imaging::PixelariumImage&) { found_ids.push_back(id); }); + pool.EnumerateResources([&found_ids](size_t id, size_t, const pixelarium::imaging::PixelariumImage&) { found_ids.push_back(id); }); EXPECT_EQ(found_ids.size(), 2); EXPECT_NE(std::find(found_ids.begin(), found_ids.end(), id1), found_ids.end()); @@ -96,7 +96,7 @@ TEST(ImageResourcePoolTest, TemplatedEnumerate) auto id2 = pool.SetResource(std::make_unique()); std::vector found_ids{}; - pool.Enumerate([&found_ids](size_t id, const pixelarium::imaging::PixelariumImage&) { found_ids.push_back(id); }); + pool.Enumerate([&found_ids](size_t id, size_t, const pixelarium::imaging::PixelariumImage&) { found_ids.push_back(id); }); EXPECT_EQ(found_ids.size(), 2); EXPECT_NE(std::find(found_ids.begin(), found_ids.end(), id1), found_ids.end());