maybe slightly better image loading maybe hopefully

This commit is contained in:
ouwou
2020-10-03 18:49:22 -04:00
parent c7a958a3bb
commit bbe36a8246
7 changed files with 105 additions and 66 deletions

View File

@@ -88,25 +88,22 @@ void ChatMessageItemContainer::UpdateContent() {
}
}
void ChatMessageItemContainer::UpdateImage() {
for (auto it = m_img_loadmap.cbegin(); it != m_img_loadmap.cend();) {
auto buf = Abaddon::Get().GetImageManager().GetFromURLIfCached(it->first);
if (buf) {
int w, h;
std::tie(w, h) = GetImageDimensions(it->second.second.Width, it->second.second.Height);
it->second.first->property_pixbuf() = buf->scale_simple(w, h, Gdk::INTERP_BILINEAR);
it = m_img_loadmap.erase(it);
} else
it++;
void ChatMessageItemContainer::UpdateImage(std::string url, Glib::RefPtr<Gdk::Pixbuf> buf) {
if (!buf) return;
if (m_embed_img != nullptr && m_embed_imgurl == url) {
int w, h;
m_embed_img->get_size_request(w, h);
m_embed_img->property_pixbuf() = buf->scale_simple(w, h, Gdk::INTERP_BILINEAR);
return;
}
if (m_embed_img != nullptr) {
auto buf = Abaddon::Get().GetImageManager().GetFromURLIfCached(m_embed_imgurl);
if (buf) {
int w, h;
m_embed_img->get_size_request(w, h);
m_embed_img->property_pixbuf() = buf->scale_simple(w, h, Gdk::INTERP_BILINEAR);
}
auto it = m_img_loadmap.find(url);
if (it != m_img_loadmap.end()) {
int w, h;
GetImageDimensions(it->second.second.Width, it->second.second.Height, w, h);
it->second.first->property_pixbuf() = buf->scale_simple(w, h, Gdk::INTERP_BILINEAR);
}
}
@@ -270,9 +267,9 @@ Gtk::EventBox *ChatMessageItemContainer::CreateEmbedComponent(const Message *dat
img->set_halign(Gtk::ALIGN_CENTER);
int w, h;
if (is_img)
std::tie(w, h) = GetImageDimensions(embed.Image.Width, embed.Image.Height, 200, 150);
GetImageDimensions(embed.Image.Width, embed.Image.Height, w, h, 200, 150);
else
std::tie(w, h) = GetImageDimensions(embed.Thumbnail.Width, embed.Thumbnail.Height, 200, 150);
GetImageDimensions(embed.Thumbnail.Width, embed.Thumbnail.Height, w, h, 200, 150);
img->set_size_request(w, h);
main->pack_start(*img);
m_embed_img = img;
@@ -319,7 +316,7 @@ Gtk::EventBox *ChatMessageItemContainer::CreateEmbedComponent(const Message *dat
Gtk::Image *ChatMessageItemContainer::CreateImageComponent(const AttachmentData &data) {
int w, h;
std::tie(w, h) = GetImageDimensions(data.Width, data.Height);
GetImageDimensions(data.Width, data.Height, w, h);
auto &im = Abaddon::Get().GetImageManager();
Gtk::Image *widget = Gtk::manage(new Gtk::Image);
@@ -350,22 +347,6 @@ Gtk::Box *ChatMessageItemContainer::CreateAttachmentComponent(const AttachmentDa
return box;
}
std::pair<int, int> ChatMessageItemContainer::GetImageDimensions(int width, int height, int clampw, int clamph) {
const auto frac = static_cast<float>(width) / height;
if (width > clampw) {
width = clampw;
height = clampw / frac;
}
if (height > clamph) {
height = clamph;
width = clamph * frac;
}
return std::make_pair(static_cast<int>(width), static_cast<int>(height));
}
void ChatMessageItemContainer::HandleImage(const AttachmentData &data, Gtk::Image *img, std::string url) {
m_img_loadmap[url] = std::make_pair(img, data);
// ask the chatwindow to call UpdateImage because dealing with lifetimes sucks

View File

@@ -13,7 +13,7 @@ public:
// attributes = edited, deleted
void UpdateAttributes();
void UpdateContent();
void UpdateImage();
void UpdateImage(std::string url, Glib::RefPtr<Gdk::Pixbuf> buf);
protected:
bool EmitImageLoad(std::string url);
@@ -24,7 +24,6 @@ protected:
Gtk::Image *CreateImageComponent(const AttachmentData &data);
Gtk::Box *CreateAttachmentComponent(const AttachmentData &data); // non-image attachments
void HandleImage(const AttachmentData &data, Gtk::Image *img, std::string url);
static std::pair<int, int> GetImageDimensions(int width, int height, int clampw = 400, int clamph = 300);
std::unordered_map<std::string, std::pair<Gtk::Image *, AttachmentData>> m_img_loadmap;

View File

@@ -210,11 +210,11 @@ void ChatWindow::ProcessNewMessage(Snowflake id, bool prepend) {
});
content->signal_image_load().connect([this, id](std::string url) {
auto &mgr = Abaddon::Get().GetImageManager();
mgr.LoadFromURL(url, [this, id](Glib::RefPtr<Gdk::Pixbuf> buf) {
mgr.LoadFromURL(url, [this, id, url](Glib::RefPtr<Gdk::Pixbuf> buf) {
if (m_id_to_widget.find(id) != m_id_to_widget.end()) {
auto *x = dynamic_cast<ChatMessageItemContainer *>(m_id_to_widget.at(id));
if (x != nullptr)
x->UpdateImage();
x->UpdateImage(url, buf);
}
});
});

View File

@@ -23,23 +23,14 @@ std::string Cache::SanitizeString(std::string str) {
}
void Cache::RespondFromPath(std::filesystem::path path, callback_type cb) {
/*if (!std::filesystem::exists(path)) return;
FILE *fp = std::fopen(path.string().c_str(), "rb");
if (fp == nullptr) return;
std::fseek(fp, 0, SEEK_END);
int len = std::ftell(fp);
std::rewind(fp);
std::vector<uint8_t> ret;
ret.resize(len);
std::fread(ret.data(), 1, len, fp);
std::fclose(fp);*/
cb(path.string());
}
void Cache::GetFileFromURL(std::string url, callback_type cb) {
auto cache_path = m_tmp_path / SanitizeString(url);
if (std::filesystem::exists(cache_path)) {
RespondFromPath(cache_path, cb);
m_futures.push_back(std::async(std::launch::async, [this, cache_path, cb]() {
RespondFromPath(cache_path, cb); }));
return;
}

View File

@@ -1,44 +1,68 @@
#include "imgmanager.hpp"
#include "util.hpp"
ImageManager::ImageManager() {
m_cb_dispatcher.connect(sigc::mem_fun(*this, &ImageManager::RunCallbacks));
}
Cache &ImageManager::GetCache() {
return m_cache;
}
void ImageManager::LoadFromURL(std::string url, std::function<void(Glib::RefPtr<Gdk::Pixbuf>)> cb) {
/*if (m_pixs.find(url) != m_pixs.end()) {
cb(m_pixs.at(url));
return;
}*/
Glib::RefPtr<Gdk::Pixbuf> ImageManager::ReadFileToPixbuf(std::string path) {
const auto &data = ReadWholeFile(path);
if (data.size() == 0) return Glib::RefPtr<Gdk::Pixbuf>(nullptr);
auto loader = Gdk::PixbufLoader::create();
loader->signal_size_prepared().connect([&loader](int w, int h) {
int cw, ch;
GetImageDimensions(w, h, cw, ch); // what could go wrong
loader->set_size(cw, ch);
});
loader->write(static_cast<const guint8 *>(data.data()), data.size());
loader->close();
auto buf = loader->get_pixbuf();
return buf;
}
void ImageManager::LoadFromURL(std::string url, callback_type cb) {
m_cache.GetFileFromURL(url, [this, url, cb](std::string path) {
try {
auto buf = Gdk::Pixbuf::create_from_file(path);
//m_pixs[url] = buf;
cb(buf);
auto buf = ReadFileToPixbuf(path);
m_cb_mutex.lock();
m_cb_queue.push(std::make_pair(buf, cb));
m_cb_dispatcher.emit();
m_cb_mutex.unlock();
} catch (std::exception &e) {
fprintf(stderr, "err loading pixbuf from %s: %s\n", path.c_str(), e.what());
}
});
}
void ImageManager::RunCallbacks() {
m_cb_mutex.lock();
const auto &pair = m_cb_queue.front();
pair.second(pair.first);
m_cb_queue.pop();
m_cb_mutex.unlock();
}
Glib::RefPtr<Gdk::Pixbuf> ImageManager::GetFromURLIfCached(std::string url) {
std::string path = m_cache.GetPathIfCached(url);
if (path != "")
return Gdk::Pixbuf::create_from_file(path);
//if (m_pixs.find(url) != m_pixs.end())
// return m_pixs.at(url);
return ReadFileToPixbuf(path);
return Glib::RefPtr<Gdk::Pixbuf>(nullptr);
}
Glib::RefPtr<Gdk::Pixbuf> ImageManager::GetPlaceholder(int size) {
std::string name = "/placeholder" + std::to_string(size);
//if (m_pixs.find(name) != m_pixs.end())
// return m_pixs.at(name);
if (m_pixs.find(name) != m_pixs.end())
return m_pixs.at(name);
try {
auto buf = Gdk::Pixbuf::create_from_file("res/decamarks.png", size, size);
// m_pixs[name] = buf;
m_pixs[name] = buf;
return buf;
} catch (std::exception &e) {
fprintf(stderr, "error loading placeholder\n");

View File

@@ -2,17 +2,30 @@
#include <string>
#include <unordered_map>
#include <functional>
#include <queue>
#include <gtkmm.h>
#include "filecache.hpp"
class ImageManager {
public:
ImageManager();
using callback_type = std::function<void(Glib::RefPtr<Gdk::Pixbuf>)>;
Cache &GetCache();
void LoadFromURL(std::string url, std::function<void(Glib::RefPtr<Gdk::Pixbuf>)> cb);
void LoadFromURL(std::string url, callback_type cb);
Glib::RefPtr<Gdk::Pixbuf> GetFromURLIfCached(std::string url);
Glib::RefPtr<Gdk::Pixbuf> GetPlaceholder(int size);
private:
Glib::RefPtr<Gdk::Pixbuf> ReadFileToPixbuf(std::string path);
mutable std::mutex m_load_mutex;
void RunCallbacks();
Glib::Dispatcher m_cb_dispatcher;
mutable std::mutex m_cb_mutex;
std::queue<std::pair<Glib::RefPtr<Gdk::Pixbuf>, callback_type>> m_cb_queue;
std::unordered_map<std::string, Glib::RefPtr<Gdk::Pixbuf>> m_pixs;
Cache m_cache;
};

View File

@@ -29,6 +29,37 @@ inline void LaunchBrowser(std::string url) {
#endif
}
inline void GetImageDimensions(int inw, int inh, int &outw, int &outh, int clampw = 400, int clamph = 300) {
const auto frac = static_cast<float>(inw) / inh;
outw = inw;
outh = inh;
if (outw > clampw) {
outw = clampw;
outh = clampw / frac;
}
if (outh > clamph) {
outh = clamph;
outw = clamph * frac;
}
}
inline std::vector<uint8_t> ReadWholeFile(std::string path) {
std::vector<uint8_t> ret;
FILE *fp = std::fopen(path.c_str(), "rb");
if (fp == nullptr)
return ret;
std::fseek(fp, 0, SEEK_END);
int len = std::ftell(fp);
std::rewind(fp);
ret.resize(len);
std::fread(ret.data(), 1, ret.size(), fp);
std::fclose(fp);
return ret;
}
inline std::string HumanReadableBytes(uint64_t bytes) {
constexpr static const char *x[] = { "B", "KB", "MB", "GB", "TB" };
int order = 0;