From 235d00192a8f0eb49c5c7368b9e1ecfdbab3160f Mon Sep 17 00:00:00 2001 From: m-aXimilian <56168660+m-aXimilian@users.noreply.github.com> Date: Tue, 23 Sep 2025 21:57:08 +0200 Subject: [PATCH] Misc Improvements (#7) * get rid of optional -> double indirection * more optional cleanup * fix * add more render pixel type options * towards different views * missing virtual declaration of ShowImage * fix runtime * init image view factory * fix build Render Image close button re-enable add readme init documentation use awesomeDoxygen ci build docs install doxygen id token permission add pages write permission --- .github/workflows/cmake.yml | 24 +++++++- CMakeLists.txt | 32 ++++++++++- CMakePresets.json | 6 +- Readme.org | 55 +++++++++++++++++++ cmake/awesomeDoxygen.cmake | 10 ++++ doc/Doxyfile.in | 15 +++++ doc/index.md | 54 ++++++++++++++++++ lib/app/CMakeLists.txt | 3 +- lib/app/DefaultApp.cpp | 3 +- lib/imaging/CMakeLists.txt | 4 ++ lib/imaging/IPixelariumImage.hpp | 21 +++++-- lib/imaging/PixelariumImageFactory.cpp | 20 ------- lib/imaging/PixelariumImageFactory.hpp | 21 +++++++ lib/imaging/impl/PixelariumCzi.cpp | 2 +- lib/imaging/impl/PixelariumCzi.hpp | 21 ++++--- lib/imaging/impl/PixelariumJpg.cpp | 2 +- lib/imaging/impl/PixelariumJpg.hpp | 19 ++----- lib/imaging/impl/PixelariumPng.cpp | 2 +- lib/imaging/impl/PixelariumPng.hpp | 19 ++----- lib/rendering/CMakeLists.txt | 7 ++- lib/rendering/CvMatRender.cpp | 43 ++++++++------- lib/rendering/IPixelariumImageView.hpp | 28 ++++++++++ lib/rendering/ImageViewFactory.cpp | 34 +++++++++--- lib/rendering/ImageViewFactory.hpp | 10 +++- lib/rendering/PixelariumImageView.hpp | 36 ------------ ...iew.cpp => PixelariumImageViewDefault.cpp} | 11 ++-- lib/rendering/PixelariumImageViewDefault.hpp | 35 ++++++++++++ lib/rendering/RenderImageManager.cpp | 1 - lib/rendering/RenderImageManager.hpp | 10 +++- lib/resources/CMakeLists.txt | 1 + lib/resources/resource.cpp | 7 +-- lib/resources/resource.hpp | 12 ++-- lib/utilities/CMakeLists.txt | 2 + tests/lib/resources/test_resource.cpp | 21 +++---- 34 files changed, 418 insertions(+), 173 deletions(-) create mode 100644 cmake/awesomeDoxygen.cmake create mode 100644 doc/Doxyfile.in create mode 100644 doc/index.md create mode 100644 lib/rendering/IPixelariumImageView.hpp delete mode 100644 lib/rendering/PixelariumImageView.hpp rename lib/rendering/{PixelariumImageView.cpp => PixelariumImageViewDefault.cpp} (89%) create mode 100644 lib/rendering/PixelariumImageViewDefault.hpp diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 4ccc5c1..3b360ea 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -9,7 +9,6 @@ on: jobs: build: - runs-on: ubuntu-latest steps: @@ -19,7 +18,7 @@ jobs: - name: setup run: | sudo apt update - sudo apt -y install libopencv-dev libglfw3 libglfw3-dev libxkbcommon-dev libxinerama-dev libxcursor-dev libxi-dev + sudo apt -y install libopencv-dev libglfw3 libglfw3-dev libxkbcommon-dev libxinerama-dev libxcursor-dev libxi-dev doxygen - name: configure run: cmake --preset gcc-debug - name: build @@ -28,3 +27,24 @@ jobs: run: | cd ${{github.workspace}}/build/gcc-debug ctest -C Debug + + - name: Upload static files as artifact + id: deployment + uses: actions/upload-pages-artifact@v3 + with: + path: ${{github.workspace}}/build/gcc-debug/doc/html + + # Deployment job + deploy: + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + runs-on: ubuntu-latest + permissions: + id-token: write + pages: write + needs: build + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/CMakeLists.txt b/CMakeLists.txt index 8dd783e..d60cbf6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -19,6 +19,12 @@ message(STATUS "GLFW:\t" ${glfw3_module_DIR}) message(STATUS "PFD:\t\t" ${pfd_DIR}) message(STATUS "SPDLOG:\t" ${spdlog_DIR}) +#==================== +# Options +option(PIXELARIUM_BUILD_UNITTESTS "Generate Unittests" ON) +option(PIXELARIUM_BUILD_DOCS "Generate Documentation" ON) +#==================== + if(WIN32) set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>") if (CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") @@ -31,6 +37,8 @@ if(UNIX) set(CMAKE_CXX_FLAGS "-Wall -Wextra -g --std=c++20") endif() +string(TOUPPER "${CMAKE_PROJECT_NAME}" PIXELARIUM_TITLE) + add_subdirectory(${pfd_DIR}) add_subdirectory(${spdlog_DIR}) add_subdirectory(${glfw3_module_DIR}) @@ -64,11 +72,31 @@ target_include_directories(${PROJECT_NAME} PUBLIC ${LIBCZI_INCLUDE_DIR} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) -option(PIXELARIUM_BUILD_UNITTESTS "Generate Unittests" ON) - if(PIXELARIUM_BUILD_UNITTESTS) include(CTest) enable_testing() add_subdirectory(tests) endif() + +if(PIXELARIUM_BUILD_DOCS) + include(${PROJECT_SOURCE_DIR}/cmake/awesomeDoxygen.cmake) + set(MAINPAGE_FILE "doc/index.md") + find_package(Doxygen) + if (DOXYGEN_FOUND) + + set(DOXYGEN_IN ${PROJECT_SOURCE_DIR}/doc/Doxyfile.in) + set(DOXYGEN_OUT ${CMAKE_BINARY_DIR}/Doxyfile) + + configure_file(${DOXYGEN_IN} ${DOXYGEN_OUT} @ONLY) + message(STATUS "Building Docs") + + add_custom_target(doxygen ALL + COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + COMMENT "Generating Docs") + + else (DOXYGEN_FOUND) + message(FATAL_ERROR "Doxygen need to be installed to generate the doxygen documentation") + endif (DOXYGEN_FOUND) +endif() diff --git a/CMakePresets.json b/CMakePresets.json index c1b9cfe..6d9e3f7 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -15,7 +15,8 @@ "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", "FETCHCONTENT_FULLY_DISCONNECTED": "OFF", "CMAKE_VERBOSE_MAKEFILE": "ON", - "PIXELARIUM_BUILD_UNITTESTS": "ON" + "PIXELARIUM_BUILD_UNITTESTS": "ON", + "PIXELARIUM_BUILD_DOCS": "OFF" } }, { @@ -51,7 +52,8 @@ "cacheVariables": { "CMAKE_C_COMPILER": "gcc", "CMAKE_CXX_COMPILER": "g++", - "CMAKE_BUILD_TYPE": "Debug" + "CMAKE_BUILD_TYPE": "Debug", + "PIXELARIUM_BUILD_DOCS": "ON" } } ], diff --git a/Readme.org b/Readme.org index a9c850c..e914807 100644 --- a/Readme.org +++ b/Readme.org @@ -6,4 +6,59 @@ #+author: Maximilian Kueffner #+exclude_tags: noexport +* Synopsis + +Pixelarium strives to be a batteries-included visualizer application to be used in conjunction with an external algorithm. +It can be linked e.g. against a library containing arbitrary functionally. Pixelarium can support viewing the results and result files of such a library. +It tries to be as flexible as possible. + +This is still work in progress and will change significantly. + +* Prerequisites + +Dependencies are either submodules in the =modules= subdirectory or artifacts of the cmake build process from the =cmake= directory. This repository should therefore be cloned recursively: +#+begin_src sh + git clone --recurse-submodules https://github.com/m-aXimilian/pixelarium.git +#+end_src + +Apart from that, this project needs OpenCV installed on the host system and available for cmake's =find_package=. + +* Building + +Given that the prerequisites are fulfilled, building can be achieved via one of the presets or by calling cmake directly. + +** Presets + +Pixelarium has a few presets setting specific compilers and configurations defined in =CMakePresets.json=. + +They can be listed by calling +#+begin_src sh :results raw :wrap src sh + cmake --list-presets +#+end_src +which will give something like +#+RESULTS: +#+begin_src sh +Available configure presets: + + "clang-release" + "clang-debug" + "gcc-release" + "gcc-debug" +#+end_src + +Building with the =clang-debug= preset would look like +#+begin_src sh + cmake --preset clang-debug + cmake --build --preset clang-debug +#+end_src + +** Direct + +If you want to specify compiler settings and options which are not defined in a preset, use cmake "directly" like +#+begin_src sh + cmake -B build -S . + cmake --build build +#+end_src + +* TODO Example diff --git a/cmake/awesomeDoxygen.cmake b/cmake/awesomeDoxygen.cmake new file mode 100644 index 0000000..e2e8f0e --- /dev/null +++ b/cmake/awesomeDoxygen.cmake @@ -0,0 +1,10 @@ +include(FetchContent) +FetchContent_Declare( + doxygen-awesome-css + URL https://github.com/jothepro/doxygen-awesome-css/archive/refs/heads/main.zip +) +FetchContent_MakeAvailable(doxygen-awesome-css) + +# Save the location the files were cloned into +# This allows us to get the path to doxygen-awesome.css +FetchContent_GetProperties(doxygen-awesome-css SOURCE_DIR AWESOME_CSS_DIR) diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in new file mode 100644 index 0000000..5e2a5aa --- /dev/null +++ b/doc/Doxyfile.in @@ -0,0 +1,15 @@ +PROJECT_NAME = @PIXELARIUM_TITLE@ + +OUTPUT_DIRECTORY = @PROJECT_BINARY_DIR@/doc +INPUT = @PROJECT_SOURCE_DIR@/lib/app \ + @PROJECT_SOURCE_DIR@/doc \ + @PROJECT_SOURCE_DIR@/lib/imaging \ + @PROJECT_SOURCE_DIR@/lib/utilities \ + @PROJECT_SOURCE_DIR@/lib/rendering \ + @PROJECT_SOURCE_DIR@/lib/resources + +DOXYFILE_ENCODING = UTF-8 +GENERATE_LATEX = NO +FULL_PATH_NAMES = NO +USE_MDFILE_AS_MAINPAGE = @PROJECT_SOURCE_DIR@/doc/index.md +HTML_EXTRA_STYLESHEET = @AWESOME_CSS_DIR@/doxygen-awesome.css \ No newline at end of file diff --git a/doc/index.md b/doc/index.md new file mode 100644 index 0000000..e9b8a9c --- /dev/null +++ b/doc/index.md @@ -0,0 +1,54 @@ +# Synopsis + +Pixelarium strives to be a batteries-included visualizer application to be used in conjunction with an external algorithm. +It can be linked e.g. against a library containing arbitrary functionally. Pixelarium can support viewing the results and result files of such a library. +It tries to be as flexible as possible. + +This is still work in progress and will change significantly. + + +# Prerequisites + +Dependencies are either submodules in the `modules` subdirectory or artifacts of the cmake build process from the `cmake` directory. This repository should therefore be cloned recursively: + + git clone --recurse-submodules https://github.com/m-aXimilian/pixelarium.git + +Apart from that, this project needs OpenCV installed on the host system and available for cmake's `find_package`. + + +# Building + +Given that the prerequisites are fulfilled, building can be achieved via one of the presets or by calling cmake directly. + + +## Presets + +Pixelarium has a few presets setting specific compilers and configurations defined in `CMakePresets.json`. + +They can be listed by calling + + cmake --list-presets + +which will give something like + + Available configure presets: + + "clang-release" + "clang-debug" + "gcc-release" + "gcc-debug" + +Building with the `clang-debug` preset would look like + + cmake --preset clang-debug + cmake --build --preset clang-debug + + +## Direct + +If you want to specify compiler settings and options which are not defined in a preset, use cmake "directly" like + + cmake -B build -S . + cmake --build build + + diff --git a/lib/app/CMakeLists.txt b/lib/app/CMakeLists.txt index 15e3103..ef41481 100644 --- a/lib/app/CMakeLists.txt +++ b/lib/app/CMakeLists.txt @@ -1,9 +1,10 @@ -set(PIXELARIUM_TITLE ${CMAKE_PROJECT_NAME}) configure_file(${CMAKE_CURRENT_SOURCE_DIR}/app_resources_default.h.in ${CMAKE_BINARY_DIR}/app_resources_default.h @ONLY) set(APPLIBSRC + AppGLFW.hpp AppGLFW.cpp + DefaultApp.hpp DefaultApp.cpp ${imgui_DIR}/imgui.cpp ${imgui_DIR}/imgui_demo.cpp diff --git a/lib/app/DefaultApp.cpp b/lib/app/DefaultApp.cpp index ce4ba1e..48a970e 100644 --- a/lib/app/DefaultApp.cpp +++ b/lib/app/DefaultApp.cpp @@ -57,7 +57,7 @@ void pixelarium::ui::DefaultApp::RenderImages() void pixelarium::ui::DefaultApp::ImageGalleryRender() { - ImGui::SetNextWindowSize(ImVec2(300, 500)); + ImGui::SetNextWindowSize(ImVec2(300, 550)); ImGui::Begin(SHOWIMAGEGALLERY, &this->image_listp_); // this updates the render collection @@ -121,7 +121,6 @@ void pixelarium::ui::DefaultApp::ImageGalleryRender() void pixelarium::ui::DefaultApp::LoadImage() { - size_t last_id{}; auto res{pfd::open_file("Load Inputs", pfd::path::home(), {"All Files", "*"}, pfd::opt::multiselect).result()}; for (auto& p : res) { diff --git a/lib/imaging/CMakeLists.txt b/lib/imaging/CMakeLists.txt index 58c0b65..620719f 100644 --- a/lib/imaging/CMakeLists.txt +++ b/lib/imaging/CMakeLists.txt @@ -7,9 +7,13 @@ message(STATUS "OpenCV_LIBs from: " ${OpenCV_LIBS}) set(IMAGELIBSRC IPixelariumImage.hpp + PixelariumImageFactory.hpp PixelariumImageFactory.cpp + impl/PixelariumJpg.hpp impl/PixelariumJpg.cpp + impl/PixelariumPng.hpp impl/PixelariumPng.cpp + impl/PixelariumCzi.hpp impl/PixelariumCzi.cpp ) diff --git a/lib/imaging/IPixelariumImage.hpp b/lib/imaging/IPixelariumImage.hpp index 6e8b334..2d3c943 100644 --- a/lib/imaging/IPixelariumImage.hpp +++ b/lib/imaging/IPixelariumImage.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include namespace pixelarium::imaging @@ -35,15 +34,27 @@ class IPixelariumImage virtual ~IPixelariumImage() = default; // this will have to throw or something for multidimensional images - virtual std::optional> TryGetImage() = 0; + virtual std::unique_ptr TryGetImage() = 0; - virtual std::optional> TryGetImage(const IImageQuery&) = 0; + virtual std::unique_ptr TryGetImage(const IImageQuery&) = 0; - virtual std::string Name() const noexcept = 0; + virtual std::vector> TryGetImages(const IImageQuery&) = 0; virtual bool Empty() const noexcept = 0; - virtual std::filesystem::path Uri() const noexcept = 0; + // default implemented + public: + virtual std::filesystem::path Uri() const noexcept { return this->uri_; } + + virtual std::string Name() const noexcept + { + if (!this->uri_.empty()) + { + return this->uri_.filename().string(); + } + + return {}; + } public: const static ImageFileType type_{ImageFileType::ABSTRACT}; diff --git a/lib/imaging/PixelariumImageFactory.cpp b/lib/imaging/PixelariumImageFactory.cpp index fad46b3..aecf01a 100644 --- a/lib/imaging/PixelariumImageFactory.cpp +++ b/lib/imaging/PixelariumImageFactory.cpp @@ -8,26 +8,6 @@ #include "impl/PixelariumJpg.hpp" #include "impl/PixelariumPng.hpp" -constexpr pixelarium::imaging::ImageFileType ExtensionToType(const std::string& extension) -{ - std::string lower_ext{extension}; - std::ranges::transform(extension, lower_ext.begin(), [](const char c) -> char { return std::tolower(c); }); - - if (lower_ext == ".jpg" || lower_ext == ".jpeg") - { - return pixelarium::imaging::ImageFileType::JPG; - } - if (lower_ext == ".png") - { - return pixelarium::imaging::ImageFileType::PNG; - } - if (lower_ext == ".czi") - { - return pixelarium::imaging::ImageFileType::CZI; - } - - return pixelarium::imaging::ImageFileType::UNKNOWN; -} /*static*/ std::unique_ptr pixelarium::imaging::PixelariumImageFactory::CreateImage(const std::string& uri) diff --git a/lib/imaging/PixelariumImageFactory.hpp b/lib/imaging/PixelariumImageFactory.hpp index 11a4b51..391e7f1 100644 --- a/lib/imaging/PixelariumImageFactory.hpp +++ b/lib/imaging/PixelariumImageFactory.hpp @@ -5,6 +5,27 @@ #include "IPixelariumImage.hpp" namespace pixelarium::imaging { +constexpr pixelarium::imaging::ImageFileType ExtensionToType(const std::string& extension) +{ + std::string lower_ext{extension}; + std::ranges::transform(extension, lower_ext.begin(), [](const char c) -> char { return std::tolower(c); }); + + if (lower_ext == ".jpg" || lower_ext == ".jpeg") + { + return pixelarium::imaging::ImageFileType::JPG; + } + if (lower_ext == ".png") + { + return pixelarium::imaging::ImageFileType::PNG; + } + if (lower_ext == ".czi") + { + return pixelarium::imaging::ImageFileType::CZI; + } + + return pixelarium::imaging::ImageFileType::UNKNOWN; +} + class PixelariumImageFactory { public: diff --git a/lib/imaging/impl/PixelariumCzi.cpp b/lib/imaging/impl/PixelariumCzi.cpp index 0489e01..9829f03 100644 --- a/lib/imaging/impl/PixelariumCzi.cpp +++ b/lib/imaging/impl/PixelariumCzi.cpp @@ -92,7 +92,7 @@ pixelarium::imaging::PixelariumCzi::PixelariumCzi(const std::string& uri) this->uri_ = std::filesystem::path(uri); } -std::optional> pixelarium::imaging::PixelariumCzi::TryGetImage() +std::unique_ptr pixelarium::imaging::PixelariumCzi::TryGetImage() { auto stream = libCZI::CreateStreamFromFile(this->uri_.wstring().c_str()); auto cziReader = libCZI::CreateCZIReader(); diff --git a/lib/imaging/impl/PixelariumCzi.hpp b/lib/imaging/impl/PixelariumCzi.hpp index e363f7d..828c679 100644 --- a/lib/imaging/impl/PixelariumCzi.hpp +++ b/lib/imaging/impl/PixelariumCzi.hpp @@ -6,6 +6,11 @@ namespace pixelarium::imaging { +struct CziParams : public IImageQuery +{ + +}; + class PixelariumCzi : public IPixelariumImage { public: @@ -13,26 +18,20 @@ class PixelariumCzi : public IPixelariumImage // IPixelariumImage member implementations public: - std::optional> TryGetImage() override; + std::unique_ptr TryGetImage() override; - std::optional> TryGetImage(const IImageQuery&) override + std::unique_ptr TryGetImage(const IImageQuery&) override { // ToDo: proper error throw std::runtime_error("Not implemented."); } - std::string Name() const noexcept override + std::vector> TryGetImages(const IImageQuery&) override { - if (!this->uri_.empty()) - { - return this->uri_.filename().string(); - } - - return {}; + // ToDo: proper error + throw std::runtime_error("Not implemented."); } - std::filesystem::path Uri() const noexcept override { return this->uri_.string(); } - bool Empty() const noexcept override { return this->is_empty_; } public: diff --git a/lib/imaging/impl/PixelariumJpg.cpp b/lib/imaging/impl/PixelariumJpg.cpp index a117ed1..be5c920 100644 --- a/lib/imaging/impl/PixelariumJpg.cpp +++ b/lib/imaging/impl/PixelariumJpg.cpp @@ -16,7 +16,7 @@ pixelarium::imaging::PixelariumJpg::PixelariumJpg(const std::string& uri) this->uri_ = std::filesystem::path(uri); } -std::optional> pixelarium::imaging::PixelariumJpg::TryGetImage() +std::unique_ptr pixelarium::imaging::PixelariumJpg::TryGetImage() { try { diff --git a/lib/imaging/impl/PixelariumJpg.hpp b/lib/imaging/impl/PixelariumJpg.hpp index b84903b..74d097e 100644 --- a/lib/imaging/impl/PixelariumJpg.hpp +++ b/lib/imaging/impl/PixelariumJpg.hpp @@ -14,27 +14,18 @@ class PixelariumJpg : public IPixelariumImage // IPixelariumImage member implementations public: - std::optional> TryGetImage() override; + std::unique_ptr TryGetImage() override; - std::optional> TryGetImage(const IImageQuery&) override + std::unique_ptr TryGetImage(const IImageQuery&) override { // ToDo: proper error throw std::runtime_error("Not possible with jpg."); } - std::string Name() const noexcept override + std::vector> TryGetImages(const IImageQuery&) override { - if (!this->uri_.empty()) - { - return this->uri_.filename().string(); - } - - return {}; - } - - std::filesystem::path Uri() const noexcept override - { - return this->uri_.string(); + // ToDo: proper error + throw std::runtime_error("Not possible with jpg."); } bool Empty() const noexcept override { return this->is_empty_; } diff --git a/lib/imaging/impl/PixelariumPng.cpp b/lib/imaging/impl/PixelariumPng.cpp index 0e6f25f..30fcdf5 100644 --- a/lib/imaging/impl/PixelariumPng.cpp +++ b/lib/imaging/impl/PixelariumPng.cpp @@ -16,7 +16,7 @@ pixelarium::imaging::PixelariumPng::PixelariumPng(const std::string& uri) this->uri_ = std::filesystem::path(uri); } -std::optional> pixelarium::imaging::PixelariumPng::TryGetImage() +std::unique_ptr pixelarium::imaging::PixelariumPng::TryGetImage() { try { diff --git a/lib/imaging/impl/PixelariumPng.hpp b/lib/imaging/impl/PixelariumPng.hpp index ff1f2af..78fa238 100644 --- a/lib/imaging/impl/PixelariumPng.hpp +++ b/lib/imaging/impl/PixelariumPng.hpp @@ -14,27 +14,18 @@ class PixelariumPng : public IPixelariumImage // IPixelariumImage member implementations public: - std::optional> TryGetImage() override; + std::unique_ptr TryGetImage() override; - std::optional> TryGetImage(const IImageQuery&) override + std::unique_ptr TryGetImage(const IImageQuery&) override { // ToDo: proper error throw std::runtime_error("Not possible with png."); } - std::string Name() const noexcept override + std::vector> TryGetImages(const IImageQuery&) override { - if (!this->uri_.empty()) - { - return this->uri_.filename().string(); - } - - return {}; - } - - std::filesystem::path Uri() const noexcept override - { - return this->uri_.string(); + // ToDo: proper error + throw std::runtime_error("Not possible with png."); } bool Empty() const noexcept override { return this->is_empty_; } diff --git a/lib/rendering/CMakeLists.txt b/lib/rendering/CMakeLists.txt index 1bf12fd..8ca53c5 100644 --- a/lib/rendering/CMakeLists.txt +++ b/lib/rendering/CMakeLists.txt @@ -1,9 +1,14 @@ set(RENDERLIBNAME pixelariumrenderlib) set(RENDERLIBSRC + CvMatRender.hpp CvMatRender.cpp + RenderImageManager.hpp RenderImageManager.cpp - PixelariumImageView.cpp + IPixelariumImageView.hpp + PixelariumImageViewDefault.hpp + PixelariumImageViewDefault.cpp + ImageViewFactory.hpp ImageViewFactory.cpp) add_library(${RENDERLIBNAME} STATIC diff --git a/lib/rendering/CvMatRender.cpp b/lib/rendering/CvMatRender.cpp index 614e62c..3bddf67 100644 --- a/lib/rendering/CvMatRender.cpp +++ b/lib/rendering/CvMatRender.cpp @@ -61,24 +61,30 @@ GLuint pixelarium::render::CvMatRender::uploadTexture() glBindTexture(GL_TEXTURE_2D, this->texture_); - // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE); - // glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); const int width = img_.cols; const int height = img_.rows; - GLenum format = (img_.type() == CV_32FC3 || img_.type() == CV_32FC1) ? GL_RGB : GL_RGBA; - GLenum type = (img_.type() == CV_16U || img_.type() == CV_16UC3) ? GL_UNSIGNED_SHORT : GL_UNSIGNED_BYTE; - GLenum internalFormat = GL_RGBA; - if (img_.type() == CV_32FC3 || img_.type() == CV_32FC1) - { - internalFormat = GL_RGB; + switch (img_.type()) { + case CV_16U: + case CV_16UC3: + case 26: + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_SHORT, img_.data); + break; + case 5: + case 29: + glPixelStorei(GL_UNPACK_ALIGNMENT, 1); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_FLOAT, img_.data); + break; + default: + glPixelStorei(GL_UNPACK_ROW_LENGTH, 0); + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, img_.cols, img_.rows, 0, GL_RGBA, GL_UNSIGNED_BYTE, img_.data); + break; } - glTexImage2D(GL_TEXTURE_2D, 0, internalFormat, width, height, 0, format, type, img_.data); - GLenum error = glGetError(); if (error != GL_NO_ERROR) { @@ -98,9 +104,9 @@ GLuint pixelarium::render::CvMatRender::Render() { return this->uploadTexture(); GLuint pixelarium::render::CvMatRender::Render(float factor) { auto res_val {this->base_->TryGetImage()}; - if (res_val.has_value()) + if (res_val) { - cv::resize(*res_val.value(), this->img_, cv::Size(0, 0), factor, factor, cv::INTER_LINEAR_EXACT); + cv::resize(*res_val, this->img_, cv::Size(0, 0), factor, factor, cv::INTER_LINEAR_EXACT); } return this->uploadTexture(); @@ -114,12 +120,12 @@ GLuint pixelarium::render::CvMatRender::Render(size_t width, size_t height) { auto res_val {this->base_->TryGetImage()}; - if (!res_val.has_value()) + if (!res_val) { return this->Render(1.0f); } - const auto sz{res_val.value()->size()}; + const auto sz{res_val->size()}; const auto get_factor = [](auto opt1, auto opt2) -> float { return opt1 < opt2 ? opt1 : opt2; }; @@ -137,16 +143,11 @@ void pixelarium::render::CvMatRender::ResetRenderImage() auto root_res = this->base_->TryGetImage(); - if (!root_res.has_value()) - { - return; - } - - if (root_res.value() == nullptr) + if (!root_res) { return; } // we copy here - this->img_ = (*root_res.value()).clone(); + this->img_ = root_res->clone(); } diff --git a/lib/rendering/IPixelariumImageView.hpp b/lib/rendering/IPixelariumImageView.hpp new file mode 100644 index 0000000..abc14e7 --- /dev/null +++ b/lib/rendering/IPixelariumImageView.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include + +#include "imaging/IPixelariumImage.hpp" +#include "rendering/CvMatRender.hpp" + +namespace pixelarium::render +{ +class IPixelariumImageView +{ + public: + virtual ~IPixelariumImageView() = default; + virtual void ShowImage() = 0; + + // default implemented + public: + virtual const bool* GetStatus() const noexcept { return &this->open_p; } + virtual void ForceUpdate() noexcept { this->is_dirty_ = true; } + + protected: + std::shared_ptr img_{}; + std::unique_ptr cached_image_{}; + render::CvMatRender render_; + bool open_p{true}; + bool is_dirty_{true}; +}; +} // namespace pixelarium::render diff --git a/lib/rendering/ImageViewFactory.cpp b/lib/rendering/ImageViewFactory.cpp index b02fb7a..acdac00 100644 --- a/lib/rendering/ImageViewFactory.cpp +++ b/lib/rendering/ImageViewFactory.cpp @@ -1,29 +1,49 @@ #include "ImageViewFactory.hpp" #include -#include +#include "imaging/PixelariumImageFactory.hpp" +#include "rendering/IPixelariumImageView.hpp" +#include "rendering/PixelariumImageViewDefault.hpp" /// @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( +std::unique_ptr pixelarium::render::ImageViewFactory::RenderImage( size_t image_id) { auto res{this->image_pool_.GetResource(image_id)}; - if (!res.has_value()) + auto img{res.lock()}; + + if (img == nullptr) { return {}; } - auto img {res.value().lock()}; - if (img->Empty()) { return {}; } - // beware: here we copy the actual image resource over to the new image - return std::make_unique(img); + auto type = imaging::ExtensionToType(img->Uri().extension().string()); + + switch (type) + { + case imaging::ImageFileType::UNKNOWN: + case imaging::ImageFileType::ABSTRACT: + return {}; + case imaging::ImageFileType::PNG: + case imaging::ImageFileType::JPG: + log_.Info("Creating a Default View"); + // beware: here we copy the actual image resource over to the new image + return std::make_unique(img); + case imaging::ImageFileType::CZI: + log_.Info("{}: Creating a CZI View"); + // beware: here we copy the actual image resource over to the new image + return std::make_unique(img); + // return std::make_unique(img); + default: + return {}; + } } diff --git a/lib/rendering/ImageViewFactory.hpp b/lib/rendering/ImageViewFactory.hpp index d809c7b..ef4c246 100644 --- a/lib/rendering/ImageViewFactory.hpp +++ b/lib/rendering/ImageViewFactory.hpp @@ -1,20 +1,24 @@ #pragma once -#include "PixelariumImageView.hpp" +#include "PixelariumImageViewDefault.hpp" +#include "rendering/IPixelariumImageView.hpp" #include "resources/resource.hpp" +#include "utilities/ILog.hpp" namespace pixelarium::render { class ImageViewFactory { using Image = imaging::IPixelariumImage; using Pool = resources::ImageResourcePool; + using Log = utils::log::ILog; public: - explicit ImageViewFactory(Pool& pool) : image_pool_(pool) {} + explicit ImageViewFactory(Pool& pool, const Log& log) : image_pool_(pool), log_(log) {} - std::unique_ptr RenderImage(size_t id); + std::unique_ptr RenderImage(size_t id); private: Pool& image_pool_; + const Log& log_; }; } // namespace pixelarium::render diff --git a/lib/rendering/PixelariumImageView.hpp b/lib/rendering/PixelariumImageView.hpp deleted file mode 100644 index de870f4..0000000 --- a/lib/rendering/PixelariumImageView.hpp +++ /dev/null @@ -1,36 +0,0 @@ -#pragma once - -#include - -#include "imaging/IPixelariumImage.hpp" -#include "imgui.h" -#include "rendering/CvMatRender.hpp" - -namespace pixelarium::render -{ -class PixelariumImageView -{ - using Image = imaging::IPixelariumImage; - using Render = render::CvMatRender; - - public: - explicit PixelariumImageView(std::shared_ptr img) : img_(img) { render_ = Render(img_); } - PixelariumImageView() = delete; - PixelariumImageView(PixelariumImageView&) = delete; - PixelariumImageView(const PixelariumImageView&) = delete; - PixelariumImageView(PixelariumImageView&&) = delete; - PixelariumImageView& operator=(PixelariumImageView&) = delete; - PixelariumImageView& operator=(PixelariumImageView&&) = delete; - - // void ToggleView(bool target) { open_p = target; } - const bool* GetStatus() const noexcept { return &this->open_p; } - void ShowImage(); - - private: - std::shared_ptr img_; - std::optional> cached_image_ {}; - Render render_; - bool open_p{true}; - ImVec2 curr_dim_{}; -}; -} // namespace pixelarium::render diff --git a/lib/rendering/PixelariumImageView.cpp b/lib/rendering/PixelariumImageViewDefault.cpp similarity index 89% rename from lib/rendering/PixelariumImageView.cpp rename to lib/rendering/PixelariumImageViewDefault.cpp index 83248bd..4f6f515 100644 --- a/lib/rendering/PixelariumImageView.cpp +++ b/lib/rendering/PixelariumImageViewDefault.cpp @@ -1,4 +1,4 @@ -#include "PixelariumImageView.hpp" +#include "PixelariumImageViewDefault.hpp" #include @@ -39,14 +39,15 @@ ImVec2 aspect_const_dimensions(const ImVec2& raw_dim, const ImVec2& curr_dim) /// 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() +void pixelarium::render::PixelariumImageViewDefault::ShowImage() { - if (!this->cached_image_.has_value()) + if (!this->cached_image_ || this->is_dirty_) { this->cached_image_ = this->img_->TryGetImage(); + this->is_dirty_ = false; } - if (this->img_->Empty() || this->img_->type_ == imaging::ImageFileType::UNKNOWN || !cached_image_.has_value() || this->img_->Name().empty()) + if (this->img_->Empty() || this->img_->type_ == imaging::ImageFileType::UNKNOWN || !cached_image_ || this->img_->Name().empty()) { // do nothing return; @@ -63,7 +64,7 @@ void pixelarium::render::PixelariumImageView::ShowImage() this->curr_dim_ = new_dim; - ImVec2 dim(cached_image_.value()->cols, cached_image_.value()->rows); + ImVec2 dim(cached_image_->cols, cached_image_->rows); ImGui::Image(reinterpret_cast(reinterpret_cast(texture)), aspect_const_dimensions(dim, new_dim)); diff --git a/lib/rendering/PixelariumImageViewDefault.hpp b/lib/rendering/PixelariumImageViewDefault.hpp new file mode 100644 index 0000000..db66cc3 --- /dev/null +++ b/lib/rendering/PixelariumImageViewDefault.hpp @@ -0,0 +1,35 @@ +#pragma once + +#include + +#include "imaging/IPixelariumImage.hpp" +#include "imgui.h" +#include "rendering/CvMatRender.hpp" +#include "rendering/IPixelariumImageView.hpp" + +namespace pixelarium::render +{ +class PixelariumImageViewDefault : public IPixelariumImageView +{ + using Image = imaging::IPixelariumImage; + using Render = render::CvMatRender; + + public: + explicit PixelariumImageViewDefault(std::shared_ptr img) + { + img_ = img; + render_ = Render(img_); + } + PixelariumImageViewDefault() = delete; + PixelariumImageViewDefault(PixelariumImageViewDefault&) = delete; + PixelariumImageViewDefault(const PixelariumImageViewDefault&) = delete; + PixelariumImageViewDefault(PixelariumImageViewDefault&&) = delete; + PixelariumImageViewDefault& operator=(PixelariumImageViewDefault&) = delete; + PixelariumImageViewDefault& operator=(PixelariumImageViewDefault&&) = delete; + + void ShowImage() override; + + private: + ImVec2 curr_dim_{}; +}; +} // namespace pixelarium::render diff --git a/lib/rendering/RenderImageManager.cpp b/lib/rendering/RenderImageManager.cpp index dc620aa..c3218c3 100644 --- a/lib/rendering/RenderImageManager.cpp +++ b/lib/rendering/RenderImageManager.cpp @@ -64,7 +64,6 @@ void pixelarium::render::RenderImageManager::Add(resources::ResourceKey key) noe RenderImageStateWrapper { .view{ std::move(current_view) }, - .show_state = current_view->GetStatus(), } } ); diff --git a/lib/rendering/RenderImageManager.hpp b/lib/rendering/RenderImageManager.hpp index d1059e2..dc88ae8 100644 --- a/lib/rendering/RenderImageManager.hpp +++ b/lib/rendering/RenderImageManager.hpp @@ -5,7 +5,8 @@ #include #include "ImageViewFactory.hpp" -#include "PixelariumImageView.hpp" +#include "PixelariumImageViewDefault.hpp" +#include "rendering/IPixelariumImageView.hpp" #include "resources/resource.hpp" #include "utilities/ILog.hpp" @@ -13,9 +14,12 @@ // aggregating views that should be rendered (or not) namespace pixelarium::render { +/// @brief Instead of directly using the view, we +/// proxy it through a wrapper. This allows for arbitrary additional data +/// to be added in future struct RenderImageStateWrapper { - std::unique_ptr view; + std::unique_ptr view; const bool* show_state; }; @@ -25,7 +29,7 @@ class RenderImageManager public: explicit RenderImageManager(Pool& pool, const utils::log::ILog& log) - : view_factory_(std::make_unique(pool)), log_(log) + : view_factory_(std::make_unique(pool, log)), log_(log) { } diff --git a/lib/resources/CMakeLists.txt b/lib/resources/CMakeLists.txt index 37c1e0e..45d5ad7 100644 --- a/lib/resources/CMakeLists.txt +++ b/lib/resources/CMakeLists.txt @@ -1,6 +1,7 @@ set(RESOURCELIBNAME pixelariumresourcelib) set(RESOURCELIBSRC + resource.hpp resource.cpp) add_library(${RESOURCELIBNAME} STATIC diff --git a/lib/resources/resource.cpp b/lib/resources/resource.cpp index c3933c0..37fa6b7 100644 --- a/lib/resources/resource.cpp +++ b/lib/resources/resource.cpp @@ -4,7 +4,6 @@ #include #include #include -#include using pixelarium::imaging::IPixelariumImage; using namespace std; @@ -22,10 +21,10 @@ 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(ResourceKey id) const +std::weak_ptr pixelarium::resources::ImageResourcePool::GetResource(ResourceKey id) const { auto search{this->resources_.find(id)}; - if (search == this->resources_.end()) return std::nullopt; + if (search == this->resources_.end()) return {}; return search->second; } @@ -81,7 +80,7 @@ bool pixelarium::resources::ImageResourcePool::DeleteResource(ResourceKey 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_) diff --git a/lib/resources/resource.hpp b/lib/resources/resource.hpp index 6582d8a..0140b63 100644 --- a/lib/resources/resource.hpp +++ b/lib/resources/resource.hpp @@ -4,7 +4,6 @@ #include #include #include -#include #include #include "imaging/IPixelariumImage.hpp" @@ -16,13 +15,14 @@ using ResourceKey = size_t; struct empty_resource_exception : public std::exception { empty_resource_exception() {}; - empty_resource_exception(const char* msg) : message_(msg) {}; - const char* what() { return message_; } + empty_resource_exception(std::string& msg) : message_(msg) {}; + const std::string& what() { return message_; } private: - const char* message_ = "Empty Resource"; + std::string message_ = "Empty Resource"; }; + struct IResource { virtual ~IResource() = default; @@ -36,7 +36,7 @@ class IResourcePool { public: virtual ~IResourcePool() = default; - virtual std::optional> GetResource(size_t id) const = 0; + virtual std::weak_ptr GetResource(size_t id) const = 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; @@ -61,7 +61,7 @@ class ImageResourcePool : public IResourcePool ImageResourcePool& operator=(ImageResourcePool&) = delete; ImageResourcePool& operator=(ImageResourcePool&&) = delete; - std::optional> GetResource(ResourceKey id) const override; + std::weak_ptr 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; diff --git a/lib/utilities/CMakeLists.txt b/lib/utilities/CMakeLists.txt index 09ac088..9228bcd 100644 --- a/lib/utilities/CMakeLists.txt +++ b/lib/utilities/CMakeLists.txt @@ -1,6 +1,8 @@ set(UTILSLIBNAME pixelariumutilslib) set(UTILSLIBSRC + ILog.hpp + SpdLogger.hpp SpdLogger.cpp) add_library(${UTILSLIBNAME} STATIC ${UTILSLIBSRC}) diff --git a/tests/lib/resources/test_resource.cpp b/tests/lib/resources/test_resource.cpp index 10a4fae..2f2972b 100644 --- a/tests/lib/resources/test_resource.cpp +++ b/tests/lib/resources/test_resource.cpp @@ -11,9 +11,11 @@ namespace class DummyImage : public pixelarium::imaging::IPixelariumImage { public: - std::optional> TryGetImage() override { return {}; } + std::unique_ptr TryGetImage() override { return {}; } - std::optional> TryGetImage(const pixelarium::imaging::IImageQuery&) override { return {}; } + std::unique_ptr TryGetImage(const pixelarium::imaging::IImageQuery&) override { return {}; } + + std::vector> TryGetImages(const pixelarium::imaging::IImageQuery&) override { return {}; } std::string Name() const noexcept override { return {}; } @@ -32,8 +34,8 @@ TEST(ImageResourcePoolTest, SetAndGetResource) auto img = std::make_unique(); auto id = pool.SetResource(std::move(img)); auto res = pool.GetResource(id); - auto res_img = res.value().lock(); - EXPECT_TRUE(res.has_value()); + auto res_img = res.lock(); + EXPECT_NE(res_img, nullptr); } @@ -43,15 +45,14 @@ TEST(ImageResourcePoolTest, SetWrappedRawPointerGet) auto img = new DummyImage(); auto id = pool.SetResource(std::unique_ptr(img)); auto res = pool.GetResource(id); - auto res_img = res.value().lock(); - EXPECT_TRUE(res.has_value()); + auto res_img = res.lock(); EXPECT_NE(res_img, nullptr); } TEST(ImageResourcePoolTest, GetNonExistentResourceReturnsEmptyOptional) { ImageResourcePool pool; - EXPECT_FALSE(pool.GetResource(12345)); + EXPECT_EQ(pool.GetResource(12345).lock(), nullptr); } TEST(ImageResourcePoolTest, ModifyResourceSuccess) @@ -61,8 +62,8 @@ TEST(ImageResourcePoolTest, ModifyResourceSuccess) auto new_img = std::make_unique(); EXPECT_TRUE(pool.ModifyResource(id, std::move(new_img))); auto res = pool.GetResource(id); - auto res_img = res.value().lock(); - EXPECT_TRUE(res.has_value()); + auto res_img = res.lock(); + EXPECT_NE(res_img, nullptr); } @@ -78,7 +79,7 @@ TEST(ImageResourcePoolTest, DeleteResourceSuccess) ImageResourcePool pool; auto id = pool.SetResource(std::make_unique()); EXPECT_TRUE(pool.DeleteResource(id)); - EXPECT_FALSE(pool.GetResource(id).has_value()); + EXPECT_EQ(pool.GetResource(id).lock(), nullptr); } TEST(ImageResourcePoolTest, DeleteResourceFail)