diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 42baa54..bd219da 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -56,6 +56,8 @@ target_sources(tomato PUBLIC ./tox_avatar_loader.cpp ./message_image_loader.hpp ./message_image_loader.cpp + ./bitset_image_loader.hpp + ./bitset_image_loader.cpp ./tox_avatar_manager.hpp ./tox_avatar_manager.cpp diff --git a/src/bitset_image_loader.cpp b/src/bitset_image_loader.cpp new file mode 100644 index 0000000..10e0260 --- /dev/null +++ b/src/bitset_image_loader.cpp @@ -0,0 +1,85 @@ +#include "./bitset_image_loader.hpp" + +#include +#include + +#include "./os_comps.hpp" + +#include + +#include + +#include + +// fwd +namespace Message { +uint64_t getTimeMS(void); +} + +BitsetImageLoader::BitsetImageLoader(void) { +} + +std::optional BitsetImageLoader::load(TextureUploaderI& tu, ObjectHandle o) { + if (!static_cast(o)) { + std::cerr << "BIL error: trying to load invalid object\n"; + return std::nullopt; + } + + if (!o.all_of()) { + // after completion, this is called until the texture times out + //std::cout << "BIL: no local have bitset\n"; + return std::nullopt; + } + + // TODO: const + auto& have = o.get().have; + + auto* surf = SDL_CreateSurfaceFrom( + have.size_bits(), 1, + SDL_PIXELFORMAT_INDEX1MSB, // LSB ? + have.data(), have.size_bytes() + ); + if (surf == nullptr) { + std::cerr << "BIL error: bitset to 1bit surface creationg failed o:" << entt::to_integral(o.entity()) << "\n"; + return std::nullopt; + } + + SDL_Color colors[] { + {0, 0, 0, 0}, + {255, 255, 255, 255}, + }; + + SDL_Palette* palette = SDL_CreatePalette(2); + SDL_SetPaletteColors(palette, colors, 0, 2); + SDL_SetSurfacePalette(surf, palette); + auto* conv_surf = SDL_ConvertSurface(surf, SDL_PIXELFORMAT_RGBA32); + + SDL_DestroySurface(surf); + SDL_DestroyPalette(palette); + + if (conv_surf == nullptr) { + std::cerr << "BIL error: surface conversion failed o:" << entt::to_integral(o.entity()) << " : " << SDL_GetError() << "\n"; + return std::nullopt; + } + + SDL_LockSurface(conv_surf); + + TextureEntry new_entry; + new_entry.timestamp_last_rendered = Message::getTimeMS(); + new_entry.current_texture = 0; + new_entry.width = have.size_bits(); + new_entry.height = 1; + + const auto n_t = tu.upload(static_cast(conv_surf->pixels), conv_surf->w, conv_surf->h, TextureUploaderI::RGBA); + assert(n_t != 0); + new_entry.textures.push_back(n_t); + new_entry.frame_duration.push_back(0); + + std::cout << "BIL: genereated bitset image o:" << entt::to_integral(o.entity()) << "\n"; + + SDL_UnlockSurface(conv_surf); + SDL_DestroySurface(conv_surf); + + return new_entry; +} + diff --git a/src/bitset_image_loader.hpp b/src/bitset_image_loader.hpp new file mode 100644 index 0000000..66e3475 --- /dev/null +++ b/src/bitset_image_loader.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include + +#include "./texture_cache.hpp" + +#include + +class BitsetImageLoader { + public: + BitsetImageLoader(void); + std::optional load(TextureUploaderI& tu, ObjectHandle o); +}; + diff --git a/src/chat_gui4.cpp b/src/chat_gui4.cpp index 90ff3eb..1898e70 100644 --- a/src/chat_gui4.cpp +++ b/src/chat_gui4.cpp @@ -1,7 +1,5 @@ #include "./chat_gui4.hpp" -#include - #include #include #include @@ -18,6 +16,7 @@ #include #include +#include #include @@ -25,6 +24,7 @@ #include "./media_meta_info_loader.hpp" #include "./sdl_clipboard_utils.hpp" +#include "os_comps.hpp" #include #include @@ -246,7 +246,19 @@ ChatGui4::ChatGui4( ContactTextureCache& contact_tc, MessageTextureCache& msg_tc, Theme& theme -) : _conf(conf), _os(os), _rmm(rmm), _cr(cr), _contact_tc(contact_tc), _msg_tc(msg_tc), _theme(theme), _sip(tu) { +) : + _conf(conf), + _os(os), + _os_sr(_os.newSubRef(this)), + _rmm(rmm), + _cr(cr), + _contact_tc(contact_tc), + _msg_tc(msg_tc), + _b_tc(_bil, tu), + _theme(theme), + _sip(tu) +{ + _os_sr.subscribe(ObjectStore_Event::object_update); } ChatGui4::~ChatGui4(void) { @@ -263,6 +275,8 @@ ChatGui4::~ChatGui4(void) { float ChatGui4::render(float time_delta) { _fss.render(); _sip.render(time_delta); + _b_tc.update(); + _b_tc.workLoadQueue(); const ImGuiViewport* viewport = ImGui::GetMainViewport(); ImGui::SetNextWindowPos(viewport->WorkPos); @@ -1162,6 +1176,8 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) { // hacky const auto* fts = o.try_get(); if (fts != nullptr && o.any_of()) { + const bool upload = o.all_of() && fts->total_down <= 0; + const int64_t total_size = o.all_of() ? o.get().file_size : @@ -1170,7 +1186,7 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) { int64_t transfer_total {0u}; float transfer_rate {0.f}; - if (o.all_of() && fts->total_down <= 0) { + if (upload) { // if have all AND no dl -> show upload progress ImGui::TextUnformatted(" up"); transfer_total = fts->total_up; @@ -1183,7 +1199,12 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) { } ImGui::SameLine(); - float fraction = float(transfer_total) / total_size; + float fraction{0.f}; + if (total_size > 0) { + fraction = float(transfer_total) / total_size; + } else if (o.all_of()) { + fraction = 1.f; + } char overlay_buf[128]; if (transfer_rate > 0.000001f) { @@ -1211,11 +1232,48 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) { std::snprintf(overlay_buf, sizeof(overlay_buf), "%.1f%%", fraction * 100 + 0.01f); } - ImGui::ProgressBar( - fraction, - {-FLT_MIN, TEXT_BASE_HEIGHT}, - overlay_buf - ); + if (!upload && !o.all_of() && o.all_of()) { + ImGui::BeginGroup(); + + // TODO: hights are all off + + ImGui::ProgressBar( + fraction, + {-FLT_MIN, TEXT_BASE_HEIGHT*0.66f}, + overlay_buf + ); + + auto const cursor_start_vec = ImGui::GetCursorScreenPos(); + const ImVec2 bar_size{ImGui::GetContentRegionAvail().x, TEXT_BASE_HEIGHT*0.15f}; + // TODO: replace with own version, so we dont have to internal + ImGui::RenderFrame( + cursor_start_vec, + { + cursor_start_vec.x + bar_size.x, + cursor_start_vec.y + bar_size.y + }, + ImGui::GetColorU32(ImGuiCol_FrameBg), + false + ); + + auto [id, img_width, img_height] = _b_tc.get(o); + ImGui::Image( + id, + bar_size, + {0.f, 0.f}, // default + {1.f, 1.f}, // default + ImGui::GetStyleColorVec4(ImGuiCol_PlotHistogram) + ); + + ImGui::EndGroup(); + //} else if (upload && o.all_of()) { + } else { + ImGui::ProgressBar( + fraction, + {-FLT_MIN, TEXT_BASE_HEIGHT}, + overlay_buf + ); + } } else { // infinite scrolling progressbar fallback ImGui::TextUnformatted(" ??"); @@ -1227,16 +1285,6 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) { ); } - if (!o.all_of() && o.all_of()) { - // texture based on have bitset - // TODO: missing have chunks/chunksize to get the correct size - - //const auto& bitest = o.get().have; - // generate 1bit sdlsurface zerocopy using bitset.data() and bitset.size_bytes() - // optionally scale down filtered (would copy) - // update texture? in cache? - } - if (o.all_of()) { const auto& frame_dims = o.get(); @@ -1721,3 +1769,12 @@ void ChatGui4::sendFileList(const std::vector& list) { } } +bool ChatGui4::onEvent(const ObjectStore::Events::ObjectUpdate& e) { + if (!e.e.all_of()) { + return false; + } + + _b_tc.stale(e.e); + return false; +} + diff --git a/src/chat_gui4.hpp b/src/chat_gui4.hpp index 15628e8..97c191b 100644 --- a/src/chat_gui4.hpp +++ b/src/chat_gui4.hpp @@ -1,6 +1,6 @@ #pragma once -#include +#include #include #include @@ -10,6 +10,7 @@ #include "./texture_cache.hpp" #include "./tox_avatar_loader.hpp" #include "./message_image_loader.hpp" +#include "./bitset_image_loader.hpp" #include "./chat_gui/file_selector.hpp" #include "./chat_gui/send_image_popup.hpp" @@ -23,15 +24,19 @@ using ContactTextureCache = TextureCache; using MessageTextureCache = TextureCache; +using BitsetTextureCache = TextureCache; -class ChatGui4 { +class ChatGui4 : public ObjectStoreEventI { ConfigModelI& _conf; ObjectStore2& _os; + ObjectStoreEventProviderI::SubscriptionReference _os_sr; RegistryMessageModelI& _rmm; Contact3Registry& _cr; ContactTextureCache& _contact_tc; MessageTextureCache& _msg_tc; + BitsetImageLoader _bil; + BitsetTextureCache _b_tc; Theme& _theme; @@ -86,6 +91,9 @@ class ChatGui4 { //bool renderSubContactListContact(const Contact3 c, const bool selected) const; void pasteFile(const char* mime_type); + + protected: + bool onEvent(const ObjectStore::Events::ObjectUpdate&) override; }; diff --git a/src/message_image_loader.hpp b/src/message_image_loader.hpp index 5dabdee..bbbfe71 100644 --- a/src/message_image_loader.hpp +++ b/src/message_image_loader.hpp @@ -12,6 +12,6 @@ class MessageImageLoader { public: MessageImageLoader(void); - std::optional load(TextureUploaderI& tu, Message3Handle c); + std::optional load(TextureUploaderI& tu, Message3Handle m); }; diff --git a/src/texture_cache.hpp b/src/texture_cache.hpp index f505936..eb0ef6b 100644 --- a/src/texture_cache.hpp +++ b/src/texture_cache.hpp @@ -91,6 +91,7 @@ struct TextureCache { entt::dense_map _cache; entt::dense_set _to_load; + // to_reload // to_update? _marked_stale? const uint64_t ms_before_purge {60 * 1000ull}; const size_t min_count_before_purge {0}; // starts purging after that @@ -134,6 +135,18 @@ struct TextureCache { } } + // markes a texture as stale and will reload it + // only if it already is loaded, does not update ts + bool stale(const KeyType& key) { + auto it = _cache.find(key); + + if (it == _cache.end()) { + return false; + } + _to_load.insert(key); + return true; + } + float update(void) { const uint64_t ts_now = Message::getTimeMS(); uint64_t ts_min_next = ts_now + ms_before_purge; @@ -165,6 +178,7 @@ struct TextureCache { } } _cache.erase(key); + _to_load.erase(key); } } @@ -172,13 +186,32 @@ struct TextureCache { bool workLoadQueue(void) { auto it = _to_load.begin(); for (; it != _to_load.end(); it++) { - auto new_entry_opt = _l.load(_tu, *it); - if (new_entry_opt.has_value()) { - _cache.emplace(*it, new_entry_opt.value()); - it = _to_load.erase(it); + if (_cache.count(*it)) { + auto new_entry_opt = _l.load(_tu, *it); + if (new_entry_opt.has_value()) { + auto old_entry = _cache.at(*it); + for (const auto& tex_id : old_entry.textures) { + _tu.destroy(tex_id); + } + auto [new_it, _] = _cache.insert_or_assign(*it, new_entry_opt.value()); + //new_it->second.current_texture = old_entry.current_texture; // ?? + new_it->second.rendered_this_frame = old_entry.rendered_this_frame; + new_it->second.timestamp_last_rendered = old_entry.timestamp_last_rendered; - // TODO: not a good idea? - break; // end load from queue/onlyload 1 per update + it = _to_load.erase(it); + + // TODO: not a good idea? + break; // end load from queue/onlyload 1 per update + } + } else { + auto new_entry_opt = _l.load(_tu, *it); + if (new_entry_opt.has_value()) { + _cache.emplace(*it, new_entry_opt.value()); + it = _to_load.erase(it); + + // TODO: not a good idea? + break; // end load from queue/onlyload 1 per update + } } }