From 2dbce14e5ef7e6404fd7446dc54cfa8220617223 Mon Sep 17 00:00:00 2001 From: Green Sky Date: Thu, 4 Dec 2025 15:56:39 +0100 Subject: [PATCH] make texture cache scale down images on load to the requested size --- src/bitset_image_loader.cpp | 2 +- src/bitset_image_loader.hpp | 2 +- src/chat_gui/contact_list.cpp | 6 ++- src/chat_gui/image_viewer_popup.cpp | 11 ++++-- src/chat_gui/image_viewer_popup.hpp | 3 ++ src/chat_gui/send_image_popup.cpp | 1 + src/chat_gui4.cpp | 6 +-- src/message_image_loader.cpp | 19 +++++++--- src/message_image_loader.hpp | 4 +- src/texture_cache.cpp | 3 ++ src/texture_cache.hpp | 59 +++++++++++++++++++++-------- src/tox_avatar_loader.cpp | 15 ++++++-- src/tox_avatar_loader.hpp | 4 +- 13 files changed, 95 insertions(+), 40 deletions(-) diff --git a/src/bitset_image_loader.cpp b/src/bitset_image_loader.cpp index 49da3a3..64c0dab 100644 --- a/src/bitset_image_loader.cpp +++ b/src/bitset_image_loader.cpp @@ -65,7 +65,7 @@ std::optional BitsetImageLoader::haveToTexture(TextureUploaderI& t BitsetImageLoader::BitsetImageLoader(void) { } -TextureLoaderResult BitsetImageLoader::load(TextureUploaderI& tu, ObjectHandle o) { +TextureLoaderResult BitsetImageLoader::load(TextureUploaderI& tu, ObjectHandle o, uint32_t w, uint32_t h) { if (!static_cast(o)) { std::cerr << "BIL error: trying to load invalid object\n"; return {}; diff --git a/src/bitset_image_loader.hpp b/src/bitset_image_loader.hpp index 348df86..15a9c25 100644 --- a/src/bitset_image_loader.hpp +++ b/src/bitset_image_loader.hpp @@ -34,7 +34,7 @@ class BitsetImageLoader { public: BitsetImageLoader(void); - TextureLoaderResult load(TextureUploaderI& tu, ObjectHandle o); + TextureLoaderResult load(TextureUploaderI& tu, ObjectHandle o, uint32_t w, uint32_t h); std::optional load(TextureUploaderI& tu, ObjectContactSub ocs); }; diff --git a/src/chat_gui/contact_list.cpp b/src/chat_gui/contact_list.cpp index 7ca1d2d..ae7f49e 100644 --- a/src/chat_gui/contact_list.cpp +++ b/src/chat_gui/contact_list.cpp @@ -41,8 +41,12 @@ void renderAvatar( } } + // TODO: per display? + // TODO: do we really need this? test dpi scaling + const auto [g_scale_x, g_scyle_y] = ImGui::GetIO().DisplayFramebufferScale; + // avatar - const auto [id, width, height] = contact_tc.get(c); + const auto [id, width, height] = contact_tc.get(c, box.x*g_scale_x, box.y*g_scyle_y); ImGui::Image( id, box, diff --git a/src/chat_gui/image_viewer_popup.cpp b/src/chat_gui/image_viewer_popup.cpp index 2fade09..c54868e 100644 --- a/src/chat_gui/image_viewer_popup.cpp +++ b/src/chat_gui/image_viewer_popup.cpp @@ -14,6 +14,8 @@ void ImageViewerPopup::view(Message3Handle m) { } _m = m; + _width = 0; + _height = 0; _open_popup = true; } @@ -35,10 +37,13 @@ void ImageViewerPopup::render(float) { ImGui::SliderFloat("scale", &_scale, 0.05f, 2.f); - auto [id, img_width, img_height] = _mtc.get(_m); + auto [id, img_width, img_height] = _mtc.get(_m, _width, _height); - img_width = std::max(5, _scale * img_width); - img_height = std::max(5, _scale * img_height); + img_width = std::max(5, _scale * img_width); + img_height = std::max(5, _scale * img_height); + + _width = img_width; + _height = img_height; ImGui::Image( id, diff --git a/src/chat_gui/image_viewer_popup.hpp b/src/chat_gui/image_viewer_popup.hpp index a8da8f1..7401e31 100644 --- a/src/chat_gui/image_viewer_popup.hpp +++ b/src/chat_gui/image_viewer_popup.hpp @@ -12,6 +12,9 @@ struct ImageViewerPopup { Message3Handle _m{}; float _scale {1.f}; + // keep track of full size from last frame + uint32_t _width {0}; + uint32_t _height {0}; bool _open_popup {false}; diff --git a/src/chat_gui/send_image_popup.cpp b/src/chat_gui/send_image_popup.cpp index d051b3d..830d090 100644 --- a/src/chat_gui/send_image_popup.cpp +++ b/src/chat_gui/send_image_popup.cpp @@ -15,6 +15,7 @@ #include #include +#include SendImagePopup::SendImagePopup(TextureUploaderI& tu) : _tu(tu) { _image_loaders.push_back(std::make_unique()); diff --git a/src/chat_gui4.cpp b/src/chat_gui4.cpp index 2370124..4d0935e 100644 --- a/src/chat_gui4.cpp +++ b/src/chat_gui4.cpp @@ -27,9 +27,7 @@ #include "./chat_gui/contact_list.hpp" -#include "./media_meta_info_loader.hpp" #include "./sdl_clipboard_utils.hpp" -#include "os_comps.hpp" #include "./string_formatter_utils.hpp" @@ -42,7 +40,7 @@ #include #include #include -#include +#include // TODO: split into msg and c namespace Components { @@ -1420,7 +1418,7 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) { if (ImGui::IsItemVisible() && o.all_of()) { ImGui::SetCursorPos(orig_curser_pos); // reset for actual img - auto [id, img_width, img_height] = _msg_tc.get(Message3Handle{reg, e}); + auto [id, img_width, img_height] = _msg_tc.get(Message3Handle{reg, e}, width, height); // if cache gives 0s, fall back to frame dims (eg if pic not loaded yet) //if (img_width == 0 || img_height == 0) { diff --git a/src/message_image_loader.cpp b/src/message_image_loader.cpp index a763cdf..6e31def 100644 --- a/src/message_image_loader.cpp +++ b/src/message_image_loader.cpp @@ -8,9 +8,8 @@ #include -#include "./os_comps.hpp" - #include +#include #include @@ -25,7 +24,7 @@ MessageImageLoader::MessageImageLoader(void) { _image_loaders.push_back(std::make_unique()); } -TextureLoaderResult MessageImageLoader::load(TextureUploaderI& tu, Message3Handle m) { +TextureLoaderResult MessageImageLoader::load(TextureUploaderI& tu, Message3Handle m, uint32_t w, uint32_t h) { if (!static_cast(m)) { return {std::nullopt}; } @@ -101,6 +100,17 @@ TextureLoaderResult MessageImageLoader::load(TextureUploaderI& tu, Message3Handl TextureEntry new_entry; new_entry.timestamp_last_rendered = getTimeMS(); new_entry.current_texture = 0; + + new_entry.src_width = res.width; + new_entry.src_height = res.height; + + if (w != 0 && h != 0 && w < res.width && h < res.height) { + res = res.scale(w, h); + } + + new_entry.width = res.width; + new_entry.height = res.height; + for (const auto& [ms, data] : res.frames) { const auto n_t = tu.upload(data.data(), res.width, res.height); if (n_t == 0) { @@ -114,9 +124,6 @@ TextureLoaderResult MessageImageLoader::load(TextureUploaderI& tu, Message3Handl continue; } - new_entry.width = res.width; - new_entry.height = res.height; - std::cout << "MIL: loaded image file o:" << /*file_path*/ entt::to_integral(o.entity()) << "\n"; return {new_entry}; diff --git a/src/message_image_loader.hpp b/src/message_image_loader.hpp index 50a52ea..7bdc565 100644 --- a/src/message_image_loader.hpp +++ b/src/message_image_loader.hpp @@ -5,13 +5,11 @@ #include "./image_loader.hpp" #include "./texture_cache.hpp" -#include - class MessageImageLoader { std::vector> _image_loaders; public: MessageImageLoader(void); - TextureLoaderResult load(TextureUploaderI& tu, Message3Handle m); + TextureLoaderResult load(TextureUploaderI& tu, Message3Handle m, uint32_t w, uint32_t h); }; diff --git a/src/texture_cache.cpp b/src/texture_cache.cpp index fa9ab08..8eb0304 100644 --- a/src/texture_cache.cpp +++ b/src/texture_cache.cpp @@ -51,8 +51,11 @@ TextureEntry generateTestAnim(TextureUploaderI& tu) { new_entry.textures.emplace_back(n_t); new_entry.frame_duration.emplace_back(250); } + // TODO: 2x2? new_entry.width = 0; new_entry.height = 0; + new_entry.src_width = 0; + new_entry.src_height = 0; return new_entry; } diff --git a/src/texture_cache.hpp b/src/texture_cache.hpp index 20cb703..712991a 100644 --- a/src/texture_cache.hpp +++ b/src/texture_cache.hpp @@ -16,6 +16,10 @@ struct TextureEntry { uint32_t width {0}; uint32_t height {0}; + + uint32_t src_width {0}; + uint32_t src_height {0}; + std::vector textures; std::vector frame_duration; // ms size_t current_texture {0}; @@ -28,6 +32,8 @@ struct TextureEntry { TextureEntry(const TextureEntry& other) : width(other.width), height(other.height), + src_width(other.src_width), + src_height(other.src_height), textures(other.textures), frame_duration(other.frame_duration), current_texture(other.current_texture), @@ -39,6 +45,8 @@ struct TextureEntry { TextureEntry& operator=(const TextureEntry& other) { width = other.width; height = other.height; + src_width = other.src_width; + src_height = other.src_height; textures = other.textures; frame_duration = other.frame_duration; current_texture = other.current_texture; @@ -97,7 +105,11 @@ struct TextureCache { TextureEntry _default_texture; entt::dense_map _cache; - entt::dense_set _to_load; + struct LoadDims { + uint32_t w{0}; + uint32_t h{0}; + }; + entt::dense_map _to_load; // to_reload // to_update? _marked_stale? const uint64_t ms_before_purge {60 * 1000ull}; @@ -126,21 +138,37 @@ struct TextureCache { uint32_t width; uint32_t height; }; - GetInfo get(const KeyType& key) { + GetInfo get(const KeyType& key, uint32_t width = 0, uint32_t height = 0) { auto it = _cache.find(key); if (it != _cache.end()) { + // if scaled down AND smaller than requested, reload larger + if ( + (width != 0 && height != 0) && + ( + (it->second.width < width && it->second.width < it->second.src_width) || + (it->second.height < height && it->second.height < it->second.src_height) + ) + ) { + // TODO: only overwrite smaller dims (or combine max) + _to_load.insert({key, LoadDims{width, height}}); + } + + // return current texture either way return { it->second.template getID(), - it->second.width, - it->second.height + it->second.src_width, + it->second.src_height }; } else { - _to_load.insert(key); + // TODO: only overwrite smaller dims (or combine max) + _to_load.insert({key, LoadDims{width, height}}); + + // return fallback return { _default_texture.getID(), - _default_texture.width, - _default_texture.height + _default_texture.src_width, + _default_texture.src_height }; } } @@ -153,7 +181,7 @@ struct TextureCache { if (it == _cache.end()) { return false; } - _to_load.insert(key); + _to_load.insert({key, {it->second.width, it->second.height}}); return true; } @@ -202,17 +230,18 @@ struct TextureCache { bool workLoadQueue(void) { auto it = _to_load.cbegin(); for (; it != _to_load.cend(); it++) { - auto new_entry_opt = _l.load(_tu, *it); - if (_cache.count(*it)) { + const auto& load_key = it->first; + auto new_entry_opt = _l.load(_tu, load_key, it->second.w, it->second.h); + if (_cache.count(load_key)) { if (new_entry_opt.texture.has_value()) { - auto old_entry = _cache.at(*it); // copy + auto old_entry = _cache.at(load_key); // copy assert(!old_entry.textures.empty()); for (const auto& tex_id : old_entry.textures) { _tu.destroy(tex_id); } - _cache.erase(*it); - auto& new_entry = _cache[*it] = new_entry_opt.texture.value(); + _cache.erase(load_key); + auto& new_entry = _cache[load_key] = new_entry_opt.texture.value(); // TODO: make update interface and let loader handle this //new_entry.current_texture = old_entry.current_texture; // ?? new_entry.rendered_this_frame = old_entry.rendered_this_frame; @@ -228,8 +257,8 @@ struct TextureCache { } } else { if (new_entry_opt.texture.has_value()) { - _cache.emplace(*it, new_entry_opt.texture.value()); - _cache.at(*it).rendered_this_frame = true; // ? + _cache.emplace(load_key, new_entry_opt.texture.value()); + _cache.at(load_key).rendered_this_frame = true; // ? it = _to_load.erase(it); // TODO: not a good idea? diff --git a/src/tox_avatar_loader.cpp b/src/tox_avatar_loader.cpp index 0fc7f6c..c688a21 100644 --- a/src/tox_avatar_loader.cpp +++ b/src/tox_avatar_loader.cpp @@ -195,7 +195,7 @@ static std::vector generateToxIdenticon(const ToxKey& key) { return pixels; } -TextureLoaderResult ToxAvatarLoader::load(TextureUploaderI& tu, Contact4 c) { +TextureLoaderResult ToxAvatarLoader::load(TextureUploaderI& tu, Contact4 c, uint32_t w, uint32_t h) { const auto& cr = _cs.registry(); if (!cr.valid(c)) { return {std::nullopt}; @@ -234,14 +234,23 @@ TextureLoaderResult ToxAvatarLoader::load(TextureUploaderI& tu, Contact4 c) { TextureEntry new_entry; new_entry.timestamp_last_rendered = getTimeMS(); new_entry.current_texture = 0; + + new_entry.src_width = res.width; + new_entry.src_height = res.height; + + if (w != 0 && h != 0 && w < res.width && h < res.height) { + res = res.scale(w, h); + } + + new_entry.width = res.width; + new_entry.height = res.height; + for (const auto& [ms, data] : res.frames) { const auto n_t = tu.upload(data.data(), res.width, res.height); new_entry.textures.push_back(n_t); new_entry.frame_duration.push_back(ms); } - new_entry.width = res.width; - new_entry.height = res.height; if (cr.all_of(c)) { std::cout << "TAL: loaded image file " << cr.get(c).file_path << "\n"; diff --git a/src/tox_avatar_loader.hpp b/src/tox_avatar_loader.hpp index f7a77cc..39b213b 100644 --- a/src/tox_avatar_loader.hpp +++ b/src/tox_avatar_loader.hpp @@ -8,8 +8,6 @@ #include "./image_loader.hpp" #include "./texture_cache.hpp" -#include - class ToxAvatarLoader { ContactStore4I& _cs; ObjectStore2& _os; @@ -21,6 +19,6 @@ class ToxAvatarLoader { public: ToxAvatarLoader(ContactStore4I& cs, ObjectStore2& os); - TextureLoaderResult load(TextureUploaderI& tu, Contact4 c); + TextureLoaderResult load(TextureUploaderI& tu, Contact4 c, uint32_t w, uint32_t h); };