load tox identicons
This commit is contained in:
parent
ef59386e5c
commit
75f78f8c7f
@ -25,6 +25,8 @@ add_executable(tomato
|
|||||||
./sdlrenderer_texture_uploader.cpp
|
./sdlrenderer_texture_uploader.cpp
|
||||||
./texture_cache.hpp
|
./texture_cache.hpp
|
||||||
./texture_cache.cpp
|
./texture_cache.cpp
|
||||||
|
./tox_avatar_loader.hpp
|
||||||
|
./tox_avatar_loader.cpp
|
||||||
./sdl_clipboard_utils.hpp
|
./sdl_clipboard_utils.hpp
|
||||||
./sdl_clipboard_utils.cpp
|
./sdl_clipboard_utils.cpp
|
||||||
./file_selector.hpp
|
./file_selector.hpp
|
||||||
|
@ -27,7 +27,7 @@ ChatGui4::ChatGui4(
|
|||||||
RegistryMessageModel& rmm,
|
RegistryMessageModel& rmm,
|
||||||
Contact3Registry& cr,
|
Contact3Registry& cr,
|
||||||
TextureUploaderI& tu
|
TextureUploaderI& tu
|
||||||
) : _conf(conf), _rmm(rmm), _cr(cr), _contact_tc(tu) {
|
) : _conf(conf), _rmm(rmm), _cr(cr), _tal(_cr), _contact_tc(_tal, tu) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void ChatGui4::render(void) {
|
void ChatGui4::render(void) {
|
||||||
@ -277,7 +277,7 @@ void ChatGui4::render(void) {
|
|||||||
_fss.render();
|
_fss.render();
|
||||||
|
|
||||||
_contact_tc.update();
|
_contact_tc.update();
|
||||||
//_tc.workLoadQueue();
|
_contact_tc.workLoadQueue();
|
||||||
}
|
}
|
||||||
|
|
||||||
// has MessageText
|
// has MessageText
|
||||||
|
@ -5,6 +5,7 @@
|
|||||||
|
|
||||||
#include "./texture_uploader.hpp"
|
#include "./texture_uploader.hpp"
|
||||||
#include "./texture_cache.hpp"
|
#include "./texture_cache.hpp"
|
||||||
|
#include "./tox_avatar_loader.hpp"
|
||||||
#include "./file_selector.hpp"
|
#include "./file_selector.hpp"
|
||||||
|
|
||||||
#include <vector>
|
#include <vector>
|
||||||
@ -15,7 +16,8 @@ class ChatGui4 {
|
|||||||
RegistryMessageModel& _rmm;
|
RegistryMessageModel& _rmm;
|
||||||
Contact3Registry& _cr;
|
Contact3Registry& _cr;
|
||||||
|
|
||||||
TextureCache<void*, Contact3> _contact_tc;
|
ToxAvatarLoader _tal;
|
||||||
|
TextureCache<void*, Contact3, ToxAvatarLoader> _contact_tc;
|
||||||
//TextureCache<Message3Handle> _msg_tc;
|
//TextureCache<Message3Handle> _msg_tc;
|
||||||
|
|
||||||
FileSelector _fss;
|
FileSelector _fss;
|
||||||
|
@ -71,15 +71,17 @@ struct TextureEntry {
|
|||||||
|
|
||||||
TextureEntry generateTestAnim(TextureUploaderI& tu);
|
TextureEntry generateTestAnim(TextureUploaderI& tu);
|
||||||
|
|
||||||
|
// TODO: move to utils or something
|
||||||
uint64_t getNowMS(void);
|
uint64_t getNowMS(void);
|
||||||
|
|
||||||
template<typename TextureType, typename KeyType>
|
template<typename TextureType, typename KeyType, class Loader>
|
||||||
struct TextureCache {
|
struct TextureCache {
|
||||||
static_assert(
|
static_assert(
|
||||||
sizeof(TextureType) == sizeof(uint64_t) ||
|
sizeof(TextureType) == sizeof(uint64_t) ||
|
||||||
sizeof(TextureType) == sizeof(uint32_t)
|
sizeof(TextureType) == sizeof(uint32_t)
|
||||||
);
|
);
|
||||||
|
|
||||||
|
Loader& _l;
|
||||||
TextureUploaderI& _tu;
|
TextureUploaderI& _tu;
|
||||||
|
|
||||||
TextureEntry _default_texture;
|
TextureEntry _default_texture;
|
||||||
@ -90,7 +92,7 @@ struct TextureCache {
|
|||||||
const uint64_t ms_before_purge {60 * 1000ull};
|
const uint64_t ms_before_purge {60 * 1000ull};
|
||||||
const size_t min_count_before_purge {0}; // starts purging after that
|
const size_t min_count_before_purge {0}; // starts purging after that
|
||||||
|
|
||||||
TextureCache(TextureUploaderI& tu) : _tu(tu) {
|
TextureCache(Loader& l, TextureUploaderI& tu) : _l(l), _tu(tu) {
|
||||||
//_image_loaders.push_back(std::make_unique<ImageLoaderWebP>());
|
//_image_loaders.push_back(std::make_unique<ImageLoaderWebP>());
|
||||||
//_image_loaders.push_back(std::make_unique<ImageLoaderSTB>());
|
//_image_loaders.push_back(std::make_unique<ImageLoaderSTB>());
|
||||||
_default_texture = generateTestAnim(_tu);
|
_default_texture = generateTestAnim(_tu);
|
||||||
@ -143,5 +145,17 @@ struct TextureCache {
|
|||||||
|
|
||||||
_default_texture.doAnimation(ts_now);
|
_default_texture.doAnimation(ts_now);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void workLoadQueue(void) {
|
||||||
|
for (auto it = _to_load.begin(); 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());
|
||||||
|
_to_load.erase(it);
|
||||||
|
// TODO: not a good idea
|
||||||
|
break; // end load from queue/onlyload 1 per update
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
140
src/tox_avatar_loader.cpp
Normal file
140
src/tox_avatar_loader.cpp
Normal file
@ -0,0 +1,140 @@
|
|||||||
|
#include "./tox_avatar_loader.hpp"
|
||||||
|
|
||||||
|
#include <solanaceae/tox_contacts/components.hpp>
|
||||||
|
|
||||||
|
#include <sodium/crypto_hash_sha256.h>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
#include <cassert>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
static float getHue_6bytes(const uint8_t* data) {
|
||||||
|
uint64_t hue_uint = 0x00;
|
||||||
|
for (size_t i = 0; i < 6; i++) {
|
||||||
|
hue_uint = hue_uint << 8;
|
||||||
|
hue_uint += data[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
// 48bit max (6bytes)
|
||||||
|
return float(hue_uint / 281474976710655.);
|
||||||
|
}
|
||||||
|
|
||||||
|
static float hue2Rgb(float p, float q, float t) {
|
||||||
|
while (t < 0.f) { t += 1.f; }
|
||||||
|
while (t > 1.f) { t -= 1.f; }
|
||||||
|
if (t < 1.f/6.f) { return p + (q - p) * 6.f * t; }
|
||||||
|
if (t < 1.f/2.f) { return q; }
|
||||||
|
if (t < 2.f/3.f) { return p + (q - p) * (4.f - 6.f * t); }
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
//https://github.com/Tox/Tox-Client-Standard/blob/master/appendix/ToxIdenticons.md
|
||||||
|
// creates a 5x5 pix texture RGBA8888
|
||||||
|
static std::vector<uint8_t> generateToxIdenticon(const ToxKey& key) {
|
||||||
|
// first hash key
|
||||||
|
std::array<uint8_t, 32> hashed_key;
|
||||||
|
assert(hashed_key.size() == key.data.size());
|
||||||
|
crypto_hash_sha256(hashed_key.data(), key.data.data(), key.data.size());
|
||||||
|
|
||||||
|
const float hue_color1 = getHue_6bytes(hashed_key.data()+26);
|
||||||
|
const float hue_color2 = getHue_6bytes(hashed_key.data()+20);
|
||||||
|
|
||||||
|
const float sat_color1 = 0.5f;
|
||||||
|
const float lig_color1 = 0.3f;
|
||||||
|
|
||||||
|
const float sat_color2 = 0.5f;
|
||||||
|
const float lig_color2 = 0.8f;
|
||||||
|
|
||||||
|
// TODO: refactor this mess
|
||||||
|
|
||||||
|
#define Q(LIG, SAT) (LIG) < 0.5f ? (LIG) * (1 + (SAT)) : (LIG) + (SAT) - (LIG) * (SAT)
|
||||||
|
const float q_color1 = Q(lig_color1, sat_color1);
|
||||||
|
const float q_color2 = Q(lig_color2, sat_color2);
|
||||||
|
#undef Q
|
||||||
|
|
||||||
|
#define P(LIG, Q) 2.f * (LIG) - (Q)
|
||||||
|
const float p_color1 = P(lig_color1, q_color1);
|
||||||
|
const float p_color2 = P(lig_color2, q_color2);
|
||||||
|
#undef P
|
||||||
|
|
||||||
|
const uint8_t color_1_r = hue2Rgb(p_color1, q_color1, hue_color1 + 1.f/3.f) * 255.f + 0.499f;
|
||||||
|
const uint8_t color_1_g = hue2Rgb(p_color1, q_color1, hue_color1) * 255.f + 0.499f;
|
||||||
|
const uint8_t color_1_b = hue2Rgb(p_color1, q_color1, hue_color1 - 1.f/3.f) * 255.f + 0.499f;
|
||||||
|
|
||||||
|
const uint8_t color_2_r = hue2Rgb(p_color2, q_color2, hue_color2 + 1.f/3.f) * 255.f + 0.499f;
|
||||||
|
const uint8_t color_2_g = hue2Rgb(p_color2, q_color2, hue_color2) * 255.f + 0.499f;
|
||||||
|
const uint8_t color_2_b = hue2Rgb(p_color2, q_color2, hue_color2 - 1.f/3.f) * 255.f + 0.499f;
|
||||||
|
|
||||||
|
// start drawing
|
||||||
|
|
||||||
|
std::vector<uint8_t> pixels(5*5*4, 0xff); // fill with white
|
||||||
|
#define R(x,y) pixels[(y*4*5) + (4*x+0)]
|
||||||
|
#define G(x,y) pixels[(y*4*5) + (4*x+1)]
|
||||||
|
#define B(x,y) pixels[(y*4*5) + (4*x+2)]
|
||||||
|
#define COLOR1(x,y) R(x, y) = color_1_r; G(x, y) = color_1_g; B(x, y) = color_1_b;
|
||||||
|
#define COLOR2(x,y) R(x, y) = color_2_r; G(x, y) = color_2_g; B(x, y) = color_2_b;
|
||||||
|
|
||||||
|
for (int64_t y = 0; y < 5; y++) {
|
||||||
|
for (int64_t x = 0; x < 5; x++) {
|
||||||
|
// mirrored index
|
||||||
|
//int64_t m_x = x <= 3 ? 3 : x - (x-2)*2;
|
||||||
|
int64_t m_x = ((x*2)-4)/2;
|
||||||
|
m_x = m_x < 0 ? -m_x : m_x;
|
||||||
|
|
||||||
|
int64_t pos = y * 3 + m_x;
|
||||||
|
const uint8_t byte = hashed_key[pos];
|
||||||
|
const uint8_t color = byte % 2;
|
||||||
|
if (color == 0) {
|
||||||
|
COLOR1(x, y)
|
||||||
|
} else {
|
||||||
|
COLOR2(x, y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#undef COLOR1
|
||||||
|
#undef COLOR2
|
||||||
|
#undef R
|
||||||
|
#undef G
|
||||||
|
#undef B
|
||||||
|
return pixels;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TextureEntry> ToxAvatarLoader::load(TextureUploaderI& tu, Contact3 c) {
|
||||||
|
if (!_cr.valid(c)) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_cr.any_of<
|
||||||
|
Contact::Components::ToxFriendPersistent,
|
||||||
|
Contact::Components::ToxGroupPersistent,
|
||||||
|
Contact::Components::ToxGroupPeerPersistent
|
||||||
|
>(c)) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> pixels;
|
||||||
|
if (_cr.all_of<Contact::Components::ToxFriendPersistent>(c)) {
|
||||||
|
pixels = generateToxIdenticon(_cr.get<Contact::Components::ToxFriendPersistent>(c).key);
|
||||||
|
} else if (_cr.all_of<Contact::Components::ToxGroupPersistent>(c)) {
|
||||||
|
pixels = generateToxIdenticon(_cr.get<Contact::Components::ToxGroupPersistent>(c).chat_id);
|
||||||
|
} else if (_cr.all_of<Contact::Components::ToxGroupPeerPersistent>(c)) {
|
||||||
|
pixels = generateToxIdenticon(_cr.get<Contact::Components::ToxGroupPeerPersistent>(c).peer_key);
|
||||||
|
}
|
||||||
|
|
||||||
|
TextureEntry new_entry;
|
||||||
|
new_entry.timestamp_last_rendered = getNowMS();
|
||||||
|
new_entry.current_texture = 0;
|
||||||
|
|
||||||
|
const auto n_t = tu.uploadRGBA(pixels.data(), 5, 5);
|
||||||
|
new_entry.textures.push_back(n_t);
|
||||||
|
new_entry.frame_duration.push_back(250);
|
||||||
|
|
||||||
|
new_entry.width = 5;
|
||||||
|
new_entry.height = 5;
|
||||||
|
|
||||||
|
std::cout << "TAL: generateToxIdenticon\n";
|
||||||
|
|
||||||
|
return new_entry;
|
||||||
|
}
|
||||||
|
|
16
src/tox_avatar_loader.hpp
Normal file
16
src/tox_avatar_loader.hpp
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <solanaceae/contact/contact_model3.hpp>
|
||||||
|
|
||||||
|
#include "./texture_cache.hpp"
|
||||||
|
|
||||||
|
#include <optional>
|
||||||
|
|
||||||
|
class ToxAvatarLoader {
|
||||||
|
Contact3Registry& _cr;
|
||||||
|
|
||||||
|
public:
|
||||||
|
ToxAvatarLoader(Contact3Registry& cr) : _cr(cr) {}
|
||||||
|
std::optional<TextureEntry> load(TextureUploaderI& tu, Contact3 c);
|
||||||
|
};
|
||||||
|
|
Loading…
Reference in New Issue
Block a user