From 0be064bb8e870179f62318f41da775c3ac666b91 Mon Sep 17 00:00:00 2001 From: m-aXimilian <56168660+m-aXimilian@users.noreply.github.com> Date: Mon, 22 Sep 2025 23:13:28 +0200 Subject: [PATCH] Gerneric image codec abstraction init (#6) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * remove raw pointer from resource manager * towards generic images * 💅 and pin libCZI module * remove raw pointer from resource manager * towards generic images * fix rendering * fix rendering * fix unit tests * fix pipeline * fix gcc build * re-enable tests * add czi impl * remove resource button * refactor user code app to being a "default app" * ui resources * missing lib? * init czi render support * typos --- .github/workflows/cmake.yml | 4 +- CMakeLists.txt | 6 -- CMakePresets.json | 35 +++++--- lib/app/CMakeLists.txt | 7 +- src/MyApp.cpp => lib/app/DefaultApp.cpp | 53 ++++++++---- src/MyApp.hpp => lib/app/DefaultApp.hpp | 6 +- lib/app/app_resources_default.h.in | 16 +++- lib/imaging/CMakeLists.txt | 7 +- lib/imaging/IPixelariumImage.hpp | 55 +++++++++++++ lib/imaging/PixelariumImage.cpp | 18 ---- lib/imaging/PixelariumImage.hpp | 85 ------------------- lib/imaging/PixelariumImageFactory.cpp | 56 +++++++++++++ lib/imaging/PixelariumImageFactory.hpp | 13 +++ lib/imaging/impl/PixelariumCzi.cpp | 105 ++++++++++++++++++++++++ lib/imaging/impl/PixelariumCzi.hpp | 46 +++++++++++ lib/imaging/impl/PixelariumJpg.cpp | 34 ++++++++ lib/imaging/impl/PixelariumJpg.hpp | 50 +++++++++++ lib/imaging/impl/PixelariumPng.cpp | 34 ++++++++ lib/imaging/impl/PixelariumPng.hpp | 50 +++++++++++ lib/rendering/CvMatRender.cpp | 44 ++++++++-- lib/rendering/CvMatRender.hpp | 10 +-- lib/rendering/ImageViewFactory.cpp | 15 +++- lib/rendering/ImageViewFactory.hpp | 3 +- lib/rendering/PixelariumImageView.cpp | 22 +++-- lib/rendering/PixelariumImageView.hpp | 9 +- lib/resources/resource.cpp | 17 ++-- lib/resources/resource.hpp | 37 ++++++--- src/CMakeLists.txt | 4 - src/app_resources_local.h.in | 12 --- src/main.cpp | 6 +- tests/lib/resources/test_resource.cpp | 32 ++++++-- 31 files changed, 670 insertions(+), 221 deletions(-) rename src/MyApp.cpp => lib/app/DefaultApp.cpp (67%) rename src/MyApp.hpp => lib/app/DefaultApp.hpp (84%) create mode 100644 lib/imaging/IPixelariumImage.hpp delete mode 100644 lib/imaging/PixelariumImage.cpp delete mode 100644 lib/imaging/PixelariumImage.hpp create mode 100644 lib/imaging/PixelariumImageFactory.cpp create mode 100644 lib/imaging/PixelariumImageFactory.hpp create mode 100644 lib/imaging/impl/PixelariumCzi.cpp create mode 100644 lib/imaging/impl/PixelariumCzi.hpp create mode 100644 lib/imaging/impl/PixelariumJpg.cpp create mode 100644 lib/imaging/impl/PixelariumJpg.hpp create mode 100644 lib/imaging/impl/PixelariumPng.cpp create mode 100644 lib/imaging/impl/PixelariumPng.hpp delete mode 100644 src/CMakeLists.txt delete mode 100644 src/app_resources_local.h.in diff --git a/.github/workflows/cmake.yml b/.github/workflows/cmake.yml index 63eaedf..4ccc5c1 100644 --- a/.github/workflows/cmake.yml +++ b/.github/workflows/cmake.yml @@ -23,8 +23,8 @@ jobs: - name: configure run: cmake --preset gcc-debug - name: build - run: cmake --build build --preset debug + run: cmake --build build --preset gcc-debug - name: test run: | - cd ${{github.workspace}}/build + cd ${{github.workspace}}/build/gcc-debug ctest -C Debug diff --git a/CMakeLists.txt b/CMakeLists.txt index 4a243cf..8dd783e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,17 +31,12 @@ if(UNIX) set(CMAKE_CXX_FLAGS "-Wall -Wextra -g --std=c++20") endif() -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG ${CMAKE_BINARY_DIR}/bin/Debug) -set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE ${CMAKE_BINARY_DIR}/bin/Release) - add_subdirectory(${pfd_DIR}) add_subdirectory(${spdlog_DIR}) add_subdirectory(${glfw3_module_DIR}) -add_subdirectory(src) add_subdirectory(lib) set(SRC - src/MyApp.cpp src/main.cpp) #==================== @@ -66,7 +61,6 @@ target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR}/lib/imaging PUBLIC ${PROJECT_SOURCE_DIR}/lib/app PUBLIC ${spdlog_DIR}/include - PUBLIC ${pfd_DIR} PUBLIC ${LIBCZI_INCLUDE_DIR} PUBLIC ${CMAKE_CURRENT_BINARY_DIR}) diff --git a/CMakePresets.json b/CMakePresets.json index 579329f..c1b9cfe 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -10,34 +10,35 @@ "name": "default", "hidden": true, "generator": "Ninja", - "binaryDir": "build", + "binaryDir": "${sourceDir}/build/${presetName}", "cacheVariables": { - "CMAKE_C_COMPILER": "clang", - "CMAKE_CXX_COMPILER": "clang++", "CMAKE_EXPORT_COMPILE_COMMANDS": "ON", - "FETCHCONTENT_FULLY_DISCONNECTED": "OFF" + "FETCHCONTENT_FULLY_DISCONNECTED": "OFF", + "CMAKE_VERBOSE_MAKEFILE": "ON", + "PIXELARIUM_BUILD_UNITTESTS": "ON" } }, { "name": "clang-release", "inherits": "default", - "binaryDir": "build/clang-release", "cacheVariables": { + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++", "CMAKE_BUILD_TYPE": "Release" } }, { "name": "clang-debug", - "binaryDir": "build/clang-debug", "inherits": "default", "cacheVariables": { + "CMAKE_C_COMPILER": "clang", + "CMAKE_CXX_COMPILER": "clang++", "CMAKE_BUILD_TYPE": "Debug" } }, { "name": "gcc-release", "inherits": "default", - "binaryDir": "build/gcc-release", "cacheVariables": { "CMAKE_C_COMPILER": "gcc", "CMAKE_CXX_COMPILER": "g++", @@ -47,7 +48,6 @@ { "name": "gcc-debug", "inherits": "default", - "binaryDir": "build/gcc-debug", "cacheVariables": { "CMAKE_C_COMPILER": "gcc", "CMAKE_CXX_COMPILER": "g++", @@ -59,18 +59,27 @@ { "name": "default", "hidden": true, - "configurePreset": "clang-debug", "jobs": 10 }, { - "name": "release", + "name": "clang-release", "inherits": "default", - "configuration": "Release" + "configurePreset": "clang-release" }, { - "name": "debug", + "name": "clang-debug", "inherits": "default", - "configuration": "Debug" + "configurePreset": "clang-debug" + }, + { + "name": "gcc-release", + "inherits": "default", + "configurePreset": "gcc-release" + }, + { + "name": "gcc-debug", + "inherits": "default", + "configurePreset": "gcc-debug" } ] } diff --git a/lib/app/CMakeLists.txt b/lib/app/CMakeLists.txt index f2f9964..15e3103 100644 --- a/lib/app/CMakeLists.txt +++ b/lib/app/CMakeLists.txt @@ -4,6 +4,7 @@ configure_file(${CMAKE_CURRENT_SOURCE_DIR}/app_resources_default.h.in set(APPLIBSRC AppGLFW.cpp + DefaultApp.cpp ${imgui_DIR}/imgui.cpp ${imgui_DIR}/imgui_demo.cpp ${imgui_DIR}/imgui_draw.cpp @@ -18,7 +19,9 @@ add_library(${APPLIBNAME} STATIC ${APPLIBSRC}) target_link_libraries(${APPLIBNAME} - PRIVATE pixelariumutilslib) + PRIVATE pixelariumutilslib + PRIVATE pixelariumimagelib + PRIVATE pixelariumrenderlib) # This needs to be public to let the consumer know about it. if(WIN32) @@ -40,5 +43,7 @@ endif() target_include_directories(${APPLIBNAME} PRIVATE ${CMAKE_BINARY_DIR} PRIVATE ${PROJECT_SOURCE_DIR}/lib + PRIVATE ${PROJECT_SOURCE_DIR}/lib/imaging + PUBLIC ${pfd_DIR} PUBLIC ${imgui_DIR} PUBLIC ${imgui_DIR}/backends) diff --git a/src/MyApp.cpp b/lib/app/DefaultApp.cpp similarity index 67% rename from src/MyApp.cpp rename to lib/app/DefaultApp.cpp index 41805fe..ce4ba1e 100644 --- a/src/MyApp.cpp +++ b/lib/app/DefaultApp.cpp @@ -1,39 +1,39 @@ -#include "MyApp.hpp" +#include "DefaultApp.hpp" #include #include #include +#include "PixelariumImageFactory.hpp" #include "app_resources_default.h" -#include "app_resources_local.h" -#include "imaging/PixelariumImage.hpp" #include "imgui.h" #include "portable-file-dialogs.h" #include "rendering/RenderImageManager.hpp" +#include "resources/resource.hpp" #include "utilities/ILog.hpp" using namespace pixelarium::imaging; -void pixelarium::ui::MyApp::MenuBarOptionsColumn1() +void pixelarium::ui::DefaultApp::MenuBarOptionsColumn1() { ImGui::MenuItem(SHOWIMGUIDEMOS, NULL, &this->demop_); ImGui::MenuItem(SHOWIMAGEGALLERY, NULL, &this->image_listp_); } -void pixelarium::ui::MyApp::MenuBarOptionsColumn2() +void pixelarium::ui::DefaultApp::MenuBarOptionsColumn2() { if (ImGui::BeginMenu(FILEMENUNAME)) { - if (ImGui::MenuItem("Load File")) + if (ImGui::MenuItem(LOADIMAGE)) { - this->LoadImageProt(); + this->LoadImage(); } ImGui::EndMenu(); } } -void pixelarium::ui::MyApp::Run() +void pixelarium::ui::DefaultApp::Run() { if (demop_) ImGui::ShowDemoWindow(&this->demop_); if (image_listp_) this->ImageGalleryRender(); @@ -41,7 +41,7 @@ void pixelarium::ui::MyApp::Run() this->RenderImages(); } -void pixelarium::ui::MyApp::RenderImages() +void pixelarium::ui::DefaultApp::RenderImages() { this->render_manager_->Enumerate( [&](resources::ResourceKey key, render::RenderImageStateWrapper& render_state) @@ -55,10 +55,10 @@ void pixelarium::ui::MyApp::RenderImages() }); } -void pixelarium::ui::MyApp::ImageGalleryRender() +void pixelarium::ui::DefaultApp::ImageGalleryRender() { ImGui::SetNextWindowSize(ImVec2(300, 500)); - ImGui::Begin(SHOWIMAGEGALLERY); + ImGui::Begin(SHOWIMAGEGALLERY, &this->image_listp_); // this updates the render collection // essentially deleting render views that were @@ -71,7 +71,7 @@ void pixelarium::ui::MyApp::ImageGalleryRender() if (ImGui::BeginListBox("Image List", ImVec2(200, 400))) { pool_.EnumerateResources( - [&](size_t id, size_t idx, const imaging::PixelariumImage& img) -> void + [&](size_t id, size_t idx, const imaging::IPixelariumImage& img) -> void { const bool is_selected = selected_index == idx; if (ImGui::Selectable(std::format("{}", img.Name()).c_str(), is_selected)) @@ -98,10 +98,28 @@ void pixelarium::ui::MyApp::ImageGalleryRender() this->render_manager_->Add(this->selected_image_); } + if (ImGui::Button(LOADIMAGE)) + { + this->LoadImage(); + } + + if (ImGui::Button(REMOVEIMAGE)) + { + this->render_manager_->MarkForDeletion(this->selected_image_); + this->pool_.DeleteResource(this->selected_image_); + } + + ImGui::SameLine(); + if (ImGui::Button(CLEARALL)) + { + this->render_manager_->Clear(); + this->pool_.Clear(); + } + ImGui::End(); // end gallery window } -void pixelarium::ui::MyApp::LoadImageProt() +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()}; @@ -109,6 +127,13 @@ void pixelarium::ui::MyApp::LoadImageProt() { this->logger_.Debug(std::format("{}: Creating image {}", __FUNCTION__, p)); - pool_.SetResource(std::make_unique(p)); + try + { + pool_.SetResource(PixelariumImageFactory::CreateImage(p)); + } + catch (pixelarium::resources::empty_resource_exception& e) + { + logger_.Error(std::format("{}: Failed to load file '{}' with '{}'", __PRETTY_FUNCTION__, p, e.what())); + } } } diff --git a/src/MyApp.hpp b/lib/app/DefaultApp.hpp similarity index 84% rename from src/MyApp.hpp rename to lib/app/DefaultApp.hpp index 4a694ef..8712254 100644 --- a/src/MyApp.hpp +++ b/lib/app/DefaultApp.hpp @@ -11,10 +11,10 @@ namespace pixelarium::ui { -class MyApp : public application::AppGLFW +class DefaultApp : public application::AppGLFW { public: - MyApp(const utils::log::ILog& log, pixelarium::resources::ImageResourcePool& pool) + DefaultApp(const utils::log::ILog& log, pixelarium::resources::ImageResourcePool& pool) : application::AppGLFW(log), pool_(pool), render_manager_(std::make_unique(pool, log)) @@ -27,7 +27,7 @@ class MyApp : public application::AppGLFW void Run() override; private: - void LoadImageProt(); + void LoadImage(); void ImageGalleryRender(); void RenderImages(); diff --git a/lib/app/app_resources_default.h.in b/lib/app/app_resources_default.h.in index e8b7159..ebc5769 100644 --- a/lib/app/app_resources_default.h.in +++ b/lib/app/app_resources_default.h.in @@ -5,9 +5,17 @@ #cmakedefine PIXELARIUM_TITLE "@PIXELARIUM_TITLE@" -#define MAINMENUNAME "Menu" -#define FILEMENUNAME "File" -#define LOGLEVELSELECT "Log Level" - +// clang-format off +#define MAINMENUNAME "Menu" +#define FILEMENUNAME "File" +#define LOGLEVELSELECT "Log Level" +#define SHOWIMGUIDEMOS "ImGui Demos" +#define SHOWIMAGEGALLERY "Image Gallery" +#define AUTOSHOWSELECTED "Auto View" +#define OPENIMAGE "Open" +#define LOADIMAGE "Load Image" +#define REMOVEIMAGE "Remove Image" +#define CLEARALL "Clear All" +// clang-format on inline constexpr std::array LOGLEVELS = {"Trace", "Debug", "Info", "Warning", "Error"}; diff --git a/lib/imaging/CMakeLists.txt b/lib/imaging/CMakeLists.txt index 078fa86..58c0b65 100644 --- a/lib/imaging/CMakeLists.txt +++ b/lib/imaging/CMakeLists.txt @@ -6,7 +6,12 @@ message(STATUS "Found opencv: " ${OpenCV_INCLUDE_DIRS}) message(STATUS "OpenCV_LIBs from: " ${OpenCV_LIBS}) set(IMAGELIBSRC - PixelariumImage.cpp) + IPixelariumImage.hpp + PixelariumImageFactory.cpp + impl/PixelariumJpg.cpp + impl/PixelariumPng.cpp + impl/PixelariumCzi.cpp +) set(IMAGELIBLIBNAME pixelariumimagelib) diff --git a/lib/imaging/IPixelariumImage.hpp b/lib/imaging/IPixelariumImage.hpp new file mode 100644 index 0000000..6e8b334 --- /dev/null +++ b/lib/imaging/IPixelariumImage.hpp @@ -0,0 +1,55 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +namespace pixelarium::imaging +{ +using ImageQueryFunctor = std::function; + +enum class ImageFileType +{ + UNKNOWN = -10, + ABSTRACT = 0, + PNG = 1, + JPG = 2, + CZI = 3, +}; + +/// @brief An abstract interface to define a semantic query +/// for a multi-dimensional image. +struct IImageQuery +{ + virtual ~IImageQuery() = default; +}; + +/// @brief This aims to be a generic image abstraction +/// meant for codec specific implementation. +class IPixelariumImage +{ + public: + virtual ~IPixelariumImage() = default; + + // this will have to throw or something for multidimensional images + virtual std::optional> TryGetImage() = 0; + + virtual std::optional> TryGetImage(const IImageQuery&) = 0; + + virtual std::string Name() const noexcept = 0; + + virtual bool Empty() const noexcept = 0; + + virtual std::filesystem::path Uri() const noexcept = 0; + + public: + const static ImageFileType type_{ImageFileType::ABSTRACT}; + + protected: + std::filesystem::path uri_; +}; + +} // namespace pixelarium::imaging diff --git a/lib/imaging/PixelariumImage.cpp b/lib/imaging/PixelariumImage.cpp deleted file mode 100644 index 1937d92..0000000 --- a/lib/imaging/PixelariumImage.cpp +++ /dev/null @@ -1,18 +0,0 @@ -#include "PixelariumImage.hpp" - -#include -#include -#include -#include -#include - -pixelarium::imaging::PixelariumImage::PixelariumImage(const std::string& uri) -{ - if (!std::filesystem::exists(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 deleted file mode 100644 index 38263b3..0000000 --- a/lib/imaging/PixelariumImage.hpp +++ /dev/null @@ -1,85 +0,0 @@ -#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); - - 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_)) {} - 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/imaging/PixelariumImageFactory.cpp b/lib/imaging/PixelariumImageFactory.cpp new file mode 100644 index 0000000..fad46b3 --- /dev/null +++ b/lib/imaging/PixelariumImageFactory.cpp @@ -0,0 +1,56 @@ +#include "PixelariumImageFactory.hpp" + +#include +#include +#include + +#include "impl/PixelariumCzi.hpp" +#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) +{ + const auto res{std::filesystem::path(uri)}; + const auto target_type{ExtensionToType(res.extension().string())}; + + switch (target_type) + { + case ImageFileType::UNKNOWN: + return {}; + break; + case ImageFileType::ABSTRACT: + return {}; + break; + case ImageFileType::PNG: + return std::make_unique(uri); + break; + case ImageFileType::JPG: + return std::make_unique(uri); + break; + case ImageFileType::CZI: + return std::make_unique(uri); + break; + } +} diff --git a/lib/imaging/PixelariumImageFactory.hpp b/lib/imaging/PixelariumImageFactory.hpp new file mode 100644 index 0000000..11a4b51 --- /dev/null +++ b/lib/imaging/PixelariumImageFactory.hpp @@ -0,0 +1,13 @@ +#pragma once + +#include + +#include "IPixelariumImage.hpp" +namespace pixelarium::imaging +{ +class PixelariumImageFactory +{ + public: + static std::unique_ptr CreateImage(const std::string& uri); +}; +} // namespace pixelarium::imaging diff --git a/lib/imaging/impl/PixelariumCzi.cpp b/lib/imaging/impl/PixelariumCzi.cpp new file mode 100644 index 0000000..0489e01 --- /dev/null +++ b/lib/imaging/impl/PixelariumCzi.cpp @@ -0,0 +1,105 @@ +#include "PixelariumCzi.hpp" + +#include +#include + +#include "libCZI.h" + +std::unique_ptr CZISubBlockToCvMat(std::shared_ptr bitmap, libCZI::PixelType pixeltype) +{ + size_t pixel_size{0}; + int target_type; + + switch (pixeltype) + { + case libCZI::PixelType::Gray8: + pixel_size = 1; + target_type = CV_8U; + break; + case libCZI::PixelType::Gray16: + pixel_size = 2; + target_type = CV_16U; + break; + case libCZI::PixelType::Bgr24: + pixel_size = 3; + target_type = CV_8UC3; + break; + case libCZI::PixelType::Bgra32: + target_type = CV_8UC4; + case libCZI::PixelType::Gray32: + target_type = CV_32S; + case libCZI::PixelType::Gray32Float: + target_type = CV_32F; + pixel_size = 4; + break; + // case libCZI::PixelType::Gray64ComplexFloat: + // case libCZI::PixelType::Gray64Float: + // target_type = + // pixel_size = 8; + // break; + default: + pixel_size = -1; + break; + } + + if (pixel_size < 0) return nullptr; + + size_t height{bitmap->GetHeight()}; + size_t width{bitmap->GetWidth()}; + auto fill_mat = std::make_unique(height, width, target_type); + + auto bitmap_info = bitmap->Lock(); + + for (size_t h{0}; h < height; ++h) + { + unsigned char* source_row = ((unsigned char*)bitmap_info.ptrDataRoi) + bitmap_info.stride * h; + unsigned char* target_row = fill_mat->ptr(h); + + for (size_t w{0}; w < width; ++w) + { + switch (pixel_size) + { + case 1: + target_row[w] = source_row[w]; + break; + case 2: + reinterpret_cast(target_row)[w] = reinterpret_cast(source_row)[w]; + break; + case 3: + target_row[3 * w] = source_row[3 * w]; + target_row[3 * w + 1] = source_row[3 * w + 1]; + target_row[3 * w + 2] = source_row[3 * w + 2]; + break; + default: + throw std::runtime_error("Unknown pixel type requested!"); + break; + } + } + } + + bitmap->Unlock(); + return fill_mat; +} + +pixelarium::imaging::PixelariumCzi::PixelariumCzi(const std::string& uri) +{ + if (!std::filesystem::exists(uri)) + { + throw std::runtime_error("Render file not found."); + } + + this->is_empty_ = false; + this->uri_ = std::filesystem::path(uri); +} + +std::optional> pixelarium::imaging::PixelariumCzi::TryGetImage() +{ + auto stream = libCZI::CreateStreamFromFile(this->uri_.wstring().c_str()); + auto cziReader = libCZI::CreateCZIReader(); + cziReader->Open(stream); + auto block = cziReader->ReadSubBlock(0); + auto bitmap = block->CreateBitmap(); + auto res = CZISubBlockToCvMat(bitmap, block->GetSubBlockInfo().pixelType); + + return res; +} diff --git a/lib/imaging/impl/PixelariumCzi.hpp b/lib/imaging/impl/PixelariumCzi.hpp new file mode 100644 index 0000000..e363f7d --- /dev/null +++ b/lib/imaging/impl/PixelariumCzi.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include + +#include "../IPixelariumImage.hpp" + +namespace pixelarium::imaging +{ +class PixelariumCzi : public IPixelariumImage +{ + public: + explicit PixelariumCzi(const std::string& uri); + + // IPixelariumImage member implementations + public: + std::optional> TryGetImage() override; + + std::optional> TryGetImage(const IImageQuery&) override + { + // ToDo: proper error + throw std::runtime_error("Not implemented."); + } + + std::string Name() const noexcept override + { + if (!this->uri_.empty()) + { + return this->uri_.filename().string(); + } + + return {}; + } + + std::filesystem::path Uri() const noexcept override { return this->uri_.string(); } + + bool Empty() const noexcept override { return this->is_empty_; } + + public: + const static ImageFileType type_{ImageFileType::CZI}; + + private: + // this should be set by each image getter + // after a new cv::Mat could be instantiated + bool is_empty_{true}; +}; +} // namespace pixelarium::imaging diff --git a/lib/imaging/impl/PixelariumJpg.cpp b/lib/imaging/impl/PixelariumJpg.cpp new file mode 100644 index 0000000..a117ed1 --- /dev/null +++ b/lib/imaging/impl/PixelariumJpg.cpp @@ -0,0 +1,34 @@ +#include "PixelariumJpg.hpp" + +#include +#include +#include +#include + +pixelarium::imaging::PixelariumJpg::PixelariumJpg(const std::string& uri) +{ + if (!std::filesystem::exists(uri)) + { + throw std::runtime_error("Render file not found."); + } + + this->is_empty_ = false; + this->uri_ = std::filesystem::path(uri); +} + +std::optional> pixelarium::imaging::PixelariumJpg::TryGetImage() +{ + try + { + auto img = std::make_unique(cv::imread(this->uri_.string())); + + this->is_empty_ = false; + + return img; + } + catch (const std::exception& e) + { + this->is_empty_ = true; + return {}; + } +} diff --git a/lib/imaging/impl/PixelariumJpg.hpp b/lib/imaging/impl/PixelariumJpg.hpp new file mode 100644 index 0000000..b84903b --- /dev/null +++ b/lib/imaging/impl/PixelariumJpg.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include +#include + +#include "../IPixelariumImage.hpp" + +namespace pixelarium::imaging +{ +class PixelariumJpg : public IPixelariumImage +{ + public: + explicit PixelariumJpg(const std::string& url); + + // IPixelariumImage member implementations + public: + std::optional> TryGetImage() override; + + std::optional> TryGetImage(const IImageQuery&) override + { + // ToDo: proper error + throw std::runtime_error("Not possible with jpg."); + } + + std::string Name() const noexcept override + { + if (!this->uri_.empty()) + { + return this->uri_.filename().string(); + } + + return {}; + } + + std::filesystem::path Uri() const noexcept override + { + return this->uri_.string(); + } + + bool Empty() const noexcept override { return this->is_empty_; } + + public: + const static ImageFileType type_{ImageFileType::JPG}; + + private: + // this should be set by each image getter + // after a new cv::Mat could be instantiated + bool is_empty_{true}; +}; +} // namespace pixelarium::imaging diff --git a/lib/imaging/impl/PixelariumPng.cpp b/lib/imaging/impl/PixelariumPng.cpp new file mode 100644 index 0000000..0e6f25f --- /dev/null +++ b/lib/imaging/impl/PixelariumPng.cpp @@ -0,0 +1,34 @@ +#include "PixelariumPng.hpp" + +#include +#include +#include +#include + +pixelarium::imaging::PixelariumPng::PixelariumPng(const std::string& uri) +{ + if (!std::filesystem::exists(uri)) + { + throw std::runtime_error("Render file not found."); + } + + this->is_empty_ = false; + this->uri_ = std::filesystem::path(uri); +} + +std::optional> pixelarium::imaging::PixelariumPng::TryGetImage() +{ + try + { + auto img = std::make_unique(cv::imread(this->uri_.string())); + + this->is_empty_ = false; + + return img; + } + catch (const std::exception& e) + { + this->is_empty_ = true; + return {}; + } +} diff --git a/lib/imaging/impl/PixelariumPng.hpp b/lib/imaging/impl/PixelariumPng.hpp new file mode 100644 index 0000000..ff1f2af --- /dev/null +++ b/lib/imaging/impl/PixelariumPng.hpp @@ -0,0 +1,50 @@ +#pragma once + +#include +#include + +#include "../IPixelariumImage.hpp" + +namespace pixelarium::imaging +{ +class PixelariumPng : public IPixelariumImage +{ + public: + explicit PixelariumPng(const std::string& url); + + // IPixelariumImage member implementations + public: + std::optional> TryGetImage() override; + + std::optional> TryGetImage(const IImageQuery&) override + { + // ToDo: proper error + throw std::runtime_error("Not possible with png."); + } + + std::string Name() const noexcept override + { + if (!this->uri_.empty()) + { + return this->uri_.filename().string(); + } + + return {}; + } + + std::filesystem::path Uri() const noexcept override + { + return this->uri_.string(); + } + + bool Empty() const noexcept override { return this->is_empty_; } + + public: + const static ImageFileType type_{ImageFileType::PNG}; + + private: + // this should be set by each image getter + // after a new cv::Mat could be instantiated + bool is_empty_{true}; +}; +} // namespace pixelarium::imaging diff --git a/lib/rendering/CvMatRender.cpp b/lib/rendering/CvMatRender.cpp index fbbc200..614e62c 100644 --- a/lib/rendering/CvMatRender.cpp +++ b/lib/rendering/CvMatRender.cpp @@ -5,13 +5,13 @@ #include #include -#include "imaging/PixelariumImage.hpp" +#include "imaging/IPixelariumImage.hpp" using namespace pixelarium::imaging; /// @brief Constructor for the CvMatRender class. /// @param img A shared pointer to the PixelariumImage to be rendered. -pixelarium::render::CvMatRender::CvMatRender(const std::shared_ptr& img) : base_(img), texture_(0) +pixelarium::render::CvMatRender::CvMatRender(std::shared_ptr& img) : base_(img), texture_(0) { // storing a copy of the to-be-rendered image // because it will be resized and filtered eventually which we absolutely @@ -33,7 +33,7 @@ pixelarium::render::CvMatRender::~CvMatRender() /// @brief Resets the render image with a new PixelariumImage. /// @param img A shared pointer to the new PixelariumImage. -void pixelarium::render::CvMatRender::ResetRenderImage(const std::shared_ptr& img) +void pixelarium::render::CvMatRender::ResetRenderImage(std::shared_ptr& img) { this->base_ = img; this->ResetRenderImage(); @@ -97,7 +97,11 @@ GLuint pixelarium::render::CvMatRender::Render() { return this->uploadTexture(); /// @return The ID of the OpenGL texture. GLuint pixelarium::render::CvMatRender::Render(float factor) { - cv::resize(this->base_->GetImage(), this->img_, cv::Size(0, 0), factor, factor, cv::INTER_LINEAR_EXACT); + auto res_val {this->base_->TryGetImage()}; + if (res_val.has_value()) + { + cv::resize(*res_val.value(), this->img_, cv::Size(0, 0), factor, factor, cv::INTER_LINEAR_EXACT); + } return this->uploadTexture(); } @@ -108,7 +112,14 @@ GLuint pixelarium::render::CvMatRender::Render(float factor) /// @return The ID of the OpenGL texture. GLuint pixelarium::render::CvMatRender::Render(size_t width, size_t height) { - const auto sz{this->base_->GetImage().size()}; + auto res_val {this->base_->TryGetImage()}; + + if (!res_val.has_value()) + { + return this->Render(1.0f); + } + + const auto sz{res_val.value()->size()}; const auto get_factor = [](auto opt1, auto opt2) -> float { return opt1 < opt2 ? opt1 : opt2; }; @@ -116,3 +127,26 @@ GLuint pixelarium::render::CvMatRender::Render(size_t width, size_t height) return this->Render(factor); } + +void pixelarium::render::CvMatRender::ResetRenderImage() +{ + if (this->base_ == nullptr) + { + return; + } + + auto root_res = this->base_->TryGetImage(); + + if (!root_res.has_value()) + { + return; + } + + if (root_res.value() == nullptr) + { + return; + } + + // we copy here + this->img_ = (*root_res.value()).clone(); +} diff --git a/lib/rendering/CvMatRender.hpp b/lib/rendering/CvMatRender.hpp index 2aea15d..8b31cb4 100644 --- a/lib/rendering/CvMatRender.hpp +++ b/lib/rendering/CvMatRender.hpp @@ -13,7 +13,7 @@ #endif #include // Will drag system OpenGL headers #endif -#include "imaging/PixelariumImage.hpp" +#include "imaging/IPixelariumImage.hpp" // clang-format on namespace pixelarium::render @@ -33,18 +33,18 @@ class CvMatRender CvMatRender& operator=(CvMatRender&) = default; CvMatRender& operator=(CvMatRender&& other) = default; ~CvMatRender(); - explicit CvMatRender(const std::shared_ptr& img); + explicit CvMatRender(std::shared_ptr& img); public: GLuint Render(); GLuint Render(float factor); GLuint Render(size_t width, size_t height); - void ResetRenderImage() { this->img_ = this->base_->GetImage().clone(); } - void ResetRenderImage(const std::shared_ptr& img); + void ResetRenderImage(); + void ResetRenderImage(std::shared_ptr& img); private: cv::Mat img_; - std::shared_ptr base_; + std::shared_ptr base_; GLuint texture_; GLuint uploadTexture(); diff --git a/lib/rendering/ImageViewFactory.cpp b/lib/rendering/ImageViewFactory.cpp index 4c457d4..b02fb7a 100644 --- a/lib/rendering/ImageViewFactory.cpp +++ b/lib/rendering/ImageViewFactory.cpp @@ -9,14 +9,21 @@ std::unique_ptr pixelarium::render::ImageViewFactory::RenderImage( size_t image_id) { - auto img{this->image_pool_.GetResource(image_id)}; + auto res{this->image_pool_.GetResource(image_id)}; - if (!img.has_value() || img.value()->Empty()) + if (!res.has_value()) { - return 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(std::make_shared(*img.value())); + return std::make_unique(img); } diff --git a/lib/rendering/ImageViewFactory.hpp b/lib/rendering/ImageViewFactory.hpp index 2346a24..d809c7b 100644 --- a/lib/rendering/ImageViewFactory.hpp +++ b/lib/rendering/ImageViewFactory.hpp @@ -1,13 +1,12 @@ #pragma once #include "PixelariumImageView.hpp" -#include "imaging/PixelariumImage.hpp" #include "resources/resource.hpp" namespace pixelarium::render { class ImageViewFactory { - using Image = imaging::PixelariumImage; + using Image = imaging::IPixelariumImage; using Pool = resources::ImageResourcePool; public: diff --git a/lib/rendering/PixelariumImageView.cpp b/lib/rendering/PixelariumImageView.cpp index 355620e..83248bd 100644 --- a/lib/rendering/PixelariumImageView.cpp +++ b/lib/rendering/PixelariumImageView.cpp @@ -2,6 +2,7 @@ #include +#include "imaging/IPixelariumImage.hpp" #include "imgui.h" /// @brief Checks if the dimensions of two ImVec2 vectors have changed significantly. @@ -23,14 +24,14 @@ static bool dim_changed_p(const ImVec2& ref_rect, const ImVec2& new_rect) /// @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) +ImVec2 aspect_const_dimensions(const ImVec2& raw_dim, 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 w_fact = (curr_dim.x / raw_dim.x); + const auto h_fact = (curr_dim.y / raw_dim.y); const auto fact = w_fact < h_fact ? w_fact : h_fact; - return ImVec2(img.GetImage().cols * fact, img.GetImage().rows * fact); + return ImVec2(raw_dim.x * fact, raw_dim.y * fact); } /// @brief Displays the image in an ImGui window. @@ -40,13 +41,18 @@ ImVec2 aspect_const_dimensions(const pixelarium::imaging::PixelariumImage& img, /// 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()) + if (!this->cached_image_.has_value()) + { + this->cached_image_ = this->img_->TryGetImage(); + } + + if (this->img_->Empty() || this->img_->type_ == imaging::ImageFileType::UNKNOWN || !cached_image_.has_value() || this->img_->Name().empty()) { // do nothing return; } - ImGui::Begin(img_->Name().c_str(), &this->open_p, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_MenuBar); + ImGui::Begin(this->img_->Name().c_str(), &this->open_p, ImGuiWindowFlags_HorizontalScrollbar | ImGuiWindowFlags_MenuBar); this->curr_dim_ = ImGui::GetContentRegionAvail(); auto new_dim = ImGui::GetContentRegionAvail(); @@ -57,10 +63,10 @@ void pixelarium::render::PixelariumImageView::ShowImage() this->curr_dim_ = new_dim; - ImVec2 dim(this->img_->GetImage().cols, this->img_->GetImage().rows); + ImVec2 dim(cached_image_.value()->cols, cached_image_.value()->rows); ImGui::Image(reinterpret_cast(reinterpret_cast(texture)), - aspect_const_dimensions(*this->img_, new_dim)); + aspect_const_dimensions(dim, new_dim)); ImGui::Separator(); ImGui::Text("%s", std::format(" Raw Dimensions W : {}, H: {}", dim.x, dim.y).c_str()); diff --git a/lib/rendering/PixelariumImageView.hpp b/lib/rendering/PixelariumImageView.hpp index f6c7355..de870f4 100644 --- a/lib/rendering/PixelariumImageView.hpp +++ b/lib/rendering/PixelariumImageView.hpp @@ -2,7 +2,7 @@ #include -#include "imaging/PixelariumImage.hpp" +#include "imaging/IPixelariumImage.hpp" #include "imgui.h" #include "rendering/CvMatRender.hpp" @@ -10,11 +10,11 @@ namespace pixelarium::render { class PixelariumImageView { - using Image = imaging::PixelariumImage; + using Image = imaging::IPixelariumImage; using Render = render::CvMatRender; public: - explicit PixelariumImageView(const std::shared_ptr& img) : img_(img) { render_ = Render(img_); } + explicit PixelariumImageView(std::shared_ptr img) : img_(img) { render_ = Render(img_); } PixelariumImageView() = delete; PixelariumImageView(PixelariumImageView&) = delete; PixelariumImageView(const PixelariumImageView&) = delete; @@ -27,7 +27,8 @@ class PixelariumImageView void ShowImage(); private: - const std::shared_ptr img_; + std::shared_ptr img_; + std::optional> cached_image_ {}; Render render_; bool open_p{true}; ImVec2 curr_dim_{}; diff --git a/lib/resources/resource.cpp b/lib/resources/resource.cpp index b4b9090..c3933c0 100644 --- a/lib/resources/resource.cpp +++ b/lib/resources/resource.cpp @@ -6,7 +6,7 @@ #include #include -using pixelarium::imaging::PixelariumImage; +using pixelarium::imaging::IPixelariumImage; using namespace std; namespace @@ -22,19 +22,24 @@ 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::optional> pixelarium::resources::ImageResourcePool::GetResource(ResourceKey id) const { auto search{this->resources_.find(id)}; if (search == this->resources_.end()) return std::nullopt; - return search->second.get(); + return search->second; } /// @brief Sets a resource in the pool. /// @param res A unique pointer to the resource to set. /// @return The ID of the new resource. -size_t pixelarium::resources::ImageResourcePool::SetResource(unique_ptr res) +size_t pixelarium::resources::ImageResourcePool::SetResource(unique_ptr res) { + if (res == nullptr) + { + throw empty_resource_exception(); + } + auto key{::GenerateId()}; { std::lock_guard guard(this->mut_); @@ -49,7 +54,7 @@ size_t pixelarium::resources::ImageResourcePool::SetResource(unique_ptr res) + std::unique_ptr res) { auto search{this->resources_.find(id)}; if (search == this->resources_.end()) return false; @@ -76,7 +81,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 b7478f0..6582d8a 100644 --- a/lib/resources/resource.hpp +++ b/lib/resources/resource.hpp @@ -7,11 +7,22 @@ #include #include -#include "imaging/PixelariumImage.hpp" +#include "imaging/IPixelariumImage.hpp" namespace pixelarium::resources { - using ResourceKey = size_t; +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_; } + + private: + const char* message_ = "Empty Resource"; +}; + struct IResource { virtual ~IResource() = default; @@ -25,12 +36,14 @@ class IResourcePool { public: virtual ~IResourcePool() = default; - virtual std::optional GetResource(size_t id) const = 0; + virtual std::optional> 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; - virtual void EnumerateResources(const std::function& func) = 0; + virtual void EnumerateResources( + const std::function& func) = 0; virtual size_t GetTotalSize() const = 0; + virtual void Clear() = 0; }; // Now with the =GetResource= method, I do not want to transfer ownership to the caller of that method. The ownership @@ -38,7 +51,7 @@ class IResourcePool // reside with the =ResourcePool=! // In fact, the intention is, that there is no way back once the =ResourcePool= took ownership of an object. // Callers can get references, but no ownership. A caller might delete a resource though. -class ImageResourcePool : public IResourcePool +class ImageResourcePool : public IResourcePool { public: ImageResourcePool() = default; @@ -48,15 +61,17 @@ class ImageResourcePool : public IResourcePool ImageResourcePool& operator=(ImageResourcePool&) = delete; ImageResourcePool& operator=(ImageResourcePool&&) = delete; - std::optional GetResource(ResourceKey id) const override; - ResourceKey SetResource(std::unique_ptr res) override; - bool ModifyResource(ResourceKey id, std::unique_ptr res) 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 Clear() override { this->resources_.clear(); } - 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}; @@ -69,7 +84,7 @@ class ImageResourcePool : public IResourcePool size_t GetTotalSize() const override { return resources_.size(); } private: - std::unordered_map> resources_; + std::unordered_map> resources_; std::mutex mut_; }; } // namespace pixelarium::resources diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt deleted file mode 100644 index d8cfb57..0000000 --- a/src/CMakeLists.txt +++ /dev/null @@ -1,4 +0,0 @@ -message(STATUS "Configuring Resources") - -configure_file(${CMAKE_CURRENT_SOURCE_DIR}/app_resources_local.h.in - ${CMAKE_BINARY_DIR}/app_resources_local.h @ONLY) diff --git a/src/app_resources_local.h.in b/src/app_resources_local.h.in deleted file mode 100644 index e47a7cb..0000000 --- a/src/app_resources_local.h.in +++ /dev/null @@ -1,12 +0,0 @@ -#pragma once - -// 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" - -// clang-format on diff --git a/src/main.cpp b/src/main.cpp index cd1d862..dad80ae 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,7 +1,6 @@ -#include #include -#include "MyApp.hpp" +#include "DefaultApp.hpp" #include "resources/resource.hpp" #include "utilities/ILog.hpp" #include "utilities/SpdLogger.hpp" @@ -10,7 +9,6 @@ int main(int argc, char** argv) { using namespace pixelarium; using namespace std; - cout << "ok\n"; unique_ptr logger; #ifdef _WIN32 logger = make_unique(string(getenv("APPDATA")) + "/pixelarium/logfile.log", "default"); @@ -22,7 +20,7 @@ int main(int argc, char** argv) logger->ChangeLevel(utils::log::LogLevel::Debug); auto image_pool{std::make_unique()}; - pixelarium::ui::MyApp app = pixelarium::ui::MyApp(*logger, *image_pool); + pixelarium::ui::DefaultApp app = pixelarium::ui::DefaultApp(*logger, *image_pool); app.Start(); } diff --git a/tests/lib/resources/test_resource.cpp b/tests/lib/resources/test_resource.cpp index 10a784c..10a4fae 100644 --- a/tests/lib/resources/test_resource.cpp +++ b/tests/lib/resources/test_resource.cpp @@ -2,15 +2,24 @@ #include -#include "imaging/PixelariumImage.hpp" #include "resources/resource.hpp" namespace { -class DummyImage : public pixelarium::imaging::PixelariumImage +// A Mock implementation for tests requiring _any_ instance of a IPixelariumImage +class DummyImage : public pixelarium::imaging::IPixelariumImage { - // Implement minimal interface if needed for test + public: + std::optional> TryGetImage() override { return {}; } + + std::optional> TryGetImage(const pixelarium::imaging::IImageQuery&) override { return {}; } + + std::string Name() const noexcept override { return {}; } + + bool Empty() const noexcept override { return true; } + + std::filesystem::path Uri() const noexcept override { return {}; } }; } // anonymous namespace @@ -23,18 +32,20 @@ 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()); - EXPECT_NE(res.value(), nullptr); + EXPECT_NE(res_img, nullptr); } TEST(ImageResourcePoolTest, SetWrappedRawPointerGet) { ImageResourcePool pool; auto img = new DummyImage(); - auto id = pool.SetResource(std::unique_ptr(img)); + auto id = pool.SetResource(std::unique_ptr(img)); auto res = pool.GetResource(id); + auto res_img = res.value().lock(); EXPECT_TRUE(res.has_value()); - EXPECT_NE(res.value(), nullptr); + EXPECT_NE(res_img, nullptr); } TEST(ImageResourcePoolTest, GetNonExistentResourceReturnsEmptyOptional) @@ -50,8 +61,9 @@ 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()); - EXPECT_NE(res.value(), nullptr); + EXPECT_NE(res_img, nullptr); } TEST(ImageResourcePoolTest, ModifyResourceFail) @@ -82,7 +94,8 @@ TEST(ImageResourcePoolTest, EnumerateResources) auto id2 = pool.SetResource(std::make_unique()); std::vector found_ids{}; - pool.EnumerateResources([&found_ids](size_t id, size_t, const pixelarium::imaging::PixelariumImage&) { found_ids.push_back(id); }); + pool.EnumerateResources([&found_ids](size_t id, size_t, const pixelarium::imaging::IPixelariumImage&) + { 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 +109,8 @@ TEST(ImageResourcePoolTest, TemplatedEnumerate) auto id2 = pool.SetResource(std::make_unique()); std::vector found_ids{}; - pool.Enumerate([&found_ids](size_t id, size_t, const pixelarium::imaging::PixelariumImage&) { found_ids.push_back(id); }); + pool.Enumerate([&found_ids](size_t id, size_t, const pixelarium::imaging::IPixelariumImage&) + { 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());