Compare commits
12 Commits
2afa7a88a5
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2dbce14e5e | ||
|
|
997e2517a3 | ||
|
|
f4350b5d70 | ||
|
|
77c36476a6 | ||
|
|
1481b3ec66 | ||
|
|
9959932267 | ||
|
|
2d536c3597 | ||
|
|
7858474d35 | ||
|
|
fe52d7cb12 | ||
|
|
571656c64f | ||
|
|
1547999ec0 | ||
|
|
e8a15a58dd |
@@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.14...3.24 FATAL_ERROR)
|
|||||||
|
|
||||||
# cmake setup begin
|
# cmake setup begin
|
||||||
project(tomato
|
project(tomato
|
||||||
VERSION 0.3.1
|
VERSION 0.3.3
|
||||||
HOMEPAGE_URL https://github.com/Green-Sky/tomato
|
HOMEPAGE_URL https://github.com/Green-Sky/tomato
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|||||||
@@ -53,6 +53,8 @@ target_sources(tomato PUBLIC
|
|||||||
./image_loader_qoi.cpp
|
./image_loader_qoi.cpp
|
||||||
./image_loader_sdl_image.hpp
|
./image_loader_sdl_image.hpp
|
||||||
./image_loader_sdl_image.cpp
|
./image_loader_sdl_image.cpp
|
||||||
|
./image_scaler.hpp
|
||||||
|
./image_scaler.cpp
|
||||||
|
|
||||||
./texture_uploader.hpp
|
./texture_uploader.hpp
|
||||||
./sdlrenderer_texture_uploader.hpp
|
./sdlrenderer_texture_uploader.hpp
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ std::optional<TextureEntry> BitsetImageLoader::haveToTexture(TextureUploaderI& t
|
|||||||
BitsetImageLoader::BitsetImageLoader(void) {
|
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<bool>(o)) {
|
if (!static_cast<bool>(o)) {
|
||||||
std::cerr << "BIL error: trying to load invalid object\n";
|
std::cerr << "BIL error: trying to load invalid object\n";
|
||||||
return {};
|
return {};
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ class BitsetImageLoader {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
BitsetImageLoader(void);
|
BitsetImageLoader(void);
|
||||||
TextureLoaderResult load(TextureUploaderI& tu, ObjectHandle o);
|
TextureLoaderResult load(TextureUploaderI& tu, ObjectHandle o, uint32_t w, uint32_t h);
|
||||||
std::optional<TextureEntry> load(TextureUploaderI& tu, ObjectContactSub ocs);
|
std::optional<TextureEntry> load(TextureUploaderI& tu, ObjectContactSub ocs);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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
|
// 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(
|
ImGui::Image(
|
||||||
id,
|
id,
|
||||||
box,
|
box,
|
||||||
|
|||||||
@@ -14,6 +14,8 @@ void ImageViewerPopup::view(Message3Handle m) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
_m = m;
|
_m = m;
|
||||||
|
_width = 0;
|
||||||
|
_height = 0;
|
||||||
|
|
||||||
_open_popup = true;
|
_open_popup = true;
|
||||||
}
|
}
|
||||||
@@ -35,10 +37,13 @@ void ImageViewerPopup::render(float) {
|
|||||||
|
|
||||||
ImGui::SliderFloat("scale", &_scale, 0.05f, 2.f);
|
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<int32_t>(5, _scale * img_width);
|
img_width = std::max<uint32_t>(5, _scale * img_width);
|
||||||
img_height = std::max<int32_t>(5, _scale * img_height);
|
img_height = std::max<uint32_t>(5, _scale * img_height);
|
||||||
|
|
||||||
|
_width = img_width;
|
||||||
|
_height = img_height;
|
||||||
|
|
||||||
ImGui::Image(
|
ImGui::Image(
|
||||||
id,
|
id,
|
||||||
|
|||||||
@@ -12,6 +12,9 @@ struct ImageViewerPopup {
|
|||||||
|
|
||||||
Message3Handle _m{};
|
Message3Handle _m{};
|
||||||
float _scale {1.f};
|
float _scale {1.f};
|
||||||
|
// keep track of full size from last frame
|
||||||
|
uint32_t _width {0};
|
||||||
|
uint32_t _height {0};
|
||||||
|
|
||||||
bool _open_popup {false};
|
bool _open_popup {false};
|
||||||
|
|
||||||
|
|||||||
@@ -13,7 +13,9 @@
|
|||||||
|
|
||||||
#include <imgui.h>
|
#include <imgui.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
#include <cmath>
|
#include <cmath>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
SendImagePopup::SendImagePopup(TextureUploaderI& tu) : _tu(tu) {
|
SendImagePopup::SendImagePopup(TextureUploaderI& tu) : _tu(tu) {
|
||||||
_image_loaders.push_back(std::make_unique<ImageLoaderSDLBMP>());
|
_image_loaders.push_back(std::make_unique<ImageLoaderSDLBMP>());
|
||||||
@@ -245,7 +247,7 @@ void SendImagePopup::render(float time_delta) {
|
|||||||
- (
|
- (
|
||||||
ImGui::GetWindowContentRegionMin().y
|
ImGui::GetWindowContentRegionMin().y
|
||||||
+ TEXT_BASE_HEIGHT*(2-1) // row of buttons (-1 bc fh inclues fontsize)
|
+ TEXT_BASE_HEIGHT*(2-1) // row of buttons (-1 bc fh inclues fontsize)
|
||||||
+ ImGui::GetFrameHeightWithSpacing()*4
|
+ ImGui::GetFrameHeightWithSpacing()*6
|
||||||
)
|
)
|
||||||
;
|
;
|
||||||
if (height > max_height) {
|
if (height > max_height) {
|
||||||
@@ -340,8 +342,7 @@ void SendImagePopup::render(float time_delta) {
|
|||||||
{ // 4 lines delimiting the crop result
|
{ // 4 lines delimiting the crop result
|
||||||
ImU32 line_color = 0xffffffff;
|
ImU32 line_color = 0xffffffff;
|
||||||
{ // calc color
|
{ // calc color
|
||||||
auto rgb = [](float x) -> ImVec4 {
|
static constexpr auto f = [](float x) {
|
||||||
auto f = [](float x) {
|
|
||||||
while (x < 0.f) {
|
while (x < 0.f) {
|
||||||
x += 1.f;
|
x += 1.f;
|
||||||
}
|
}
|
||||||
@@ -356,7 +357,7 @@ void SendImagePopup::render(float time_delta) {
|
|||||||
return 0.f;
|
return 0.f;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
auto rgb = [](float x) -> ImVec4 {
|
||||||
float red = f(x);
|
float red = f(x);
|
||||||
float green = f(x - (1.f/3));
|
float green = f(x - (1.f/3));
|
||||||
float blue = f(x - (2.f/3));
|
float blue = f(x - (2.f/3));
|
||||||
@@ -434,7 +435,7 @@ void SendImagePopup::render(float time_delta) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const bool cropped = crop_rect.x != 0 || crop_rect.y != 0 || crop_rect.w != original_image.width || crop_rect.h != original_image.height;
|
const bool cropped = crop_rect.x != 0 || crop_rect.y != 0 || crop_rect.w != int64_t(original_image.width) || crop_rect.h != int64_t(original_image.height);
|
||||||
if (cropping) {
|
if (cropping) {
|
||||||
if (ImGui::Button("done")) {
|
if (ImGui::Button("done")) {
|
||||||
cropping = false;
|
cropping = false;
|
||||||
@@ -445,7 +446,7 @@ void SendImagePopup::render(float time_delta) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("reset")) {
|
if (ImGui::Button("reset##crop")) {
|
||||||
crop_rect.x = 0;
|
crop_rect.x = 0;
|
||||||
crop_rect.y = 0;
|
crop_rect.y = 0;
|
||||||
crop_rect.w = original_image.width;
|
crop_rect.w = original_image.width;
|
||||||
@@ -455,6 +456,44 @@ void SendImagePopup::render(float time_delta) {
|
|||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
ImGui::Text("x:%d y:%d w:%d h:%d", crop_rect.x, crop_rect.y, crop_rect.w, crop_rect.h);
|
ImGui::Text("x:%d y:%d w:%d h:%d", crop_rect.x, crop_rect.y, crop_rect.w, crop_rect.h);
|
||||||
|
|
||||||
|
ImGui::TextUnformatted("scale");
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("reset##scale")) {
|
||||||
|
scale_x = 1.f;
|
||||||
|
scale_y = 1.f;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(TEXT_BASE_HEIGHT*3);
|
||||||
|
if (ImGui::DragFloat("##scale x", &scale_x, 0.001f, 0.001f, 100.f) && scale_tie) {
|
||||||
|
scale_y = scale_x;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::TextUnformatted("X");
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::SetNextItemWidth(TEXT_BASE_HEIGHT*3);
|
||||||
|
if (ImGui::DragFloat("##scale y", &scale_y, 0.001f, 0.001f, 100.f) && scale_tie) {
|
||||||
|
scale_x = scale_y;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("/2##scale")) {
|
||||||
|
scale_x *= 0.5f;
|
||||||
|
scale_y *= 0.5f;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("*2##scale")) {
|
||||||
|
scale_x *= 2.f;
|
||||||
|
scale_y *= 2.f;
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
ImGui::Checkbox("tie", &scale_tie);
|
||||||
|
|
||||||
|
if (scale_x <= 0.f) { scale_x = 0.001f; }
|
||||||
|
if (scale_y <= 0.f) { scale_y = 0.001f; }
|
||||||
|
if (scale_x > 100.f) { scale_x = 100.f; }
|
||||||
|
if (scale_y > 100.f) { scale_y = 100.f; }
|
||||||
|
|
||||||
|
ImGui::Text("final size -> w:%d h:%d", (int)std::ceil(crop_rect.w*scale_x), (int)std::ceil(crop_rect.h*scale_y));
|
||||||
|
|
||||||
bool recalc_size = false;
|
bool recalc_size = false;
|
||||||
if (cropped) {
|
if (cropped) {
|
||||||
if (!compress) {
|
if (!compress) {
|
||||||
@@ -463,6 +502,13 @@ void SendImagePopup::render(float time_delta) {
|
|||||||
}
|
}
|
||||||
compress = true;
|
compress = true;
|
||||||
}
|
}
|
||||||
|
if (scale_x != 1.f || scale_y != 1.f) {
|
||||||
|
if (!compress) {
|
||||||
|
// looks like a change
|
||||||
|
recalc_size = true;
|
||||||
|
}
|
||||||
|
compress = true;
|
||||||
|
}
|
||||||
|
|
||||||
recalc_size |= ImGui::Checkbox("compress", &compress);
|
recalc_size |= ImGui::Checkbox("compress", &compress);
|
||||||
if (cropped && ImGui::IsItemHovered()) {
|
if (cropped && ImGui::IsItemHovered()) {
|
||||||
@@ -505,11 +551,10 @@ void SendImagePopup::render(float time_delta) {
|
|||||||
}
|
}
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
if (ImGui::Button("send ->", {-FLT_MIN, TEXT_BASE_HEIGHT*2})) {
|
if (ImGui::Button("send ->", {-FLT_MIN, TEXT_BASE_HEIGHT*2})) {
|
||||||
if (compress || cropped) {
|
if (compress || cropped || scale_x != 1.f || scale_y != 1.f) {
|
||||||
// TODO: copy bad
|
// TODO: copy bad
|
||||||
ImageLoaderI::ImageResult tmp_img;
|
ImageLoaderI::ImageResult tmp_img;
|
||||||
if (cropped) {
|
if (cropped) {
|
||||||
std::cout << "SIP: CROP!!!!!\n";
|
|
||||||
tmp_img = original_image.crop(
|
tmp_img = original_image.crop(
|
||||||
crop_rect.x,
|
crop_rect.x,
|
||||||
crop_rect.y,
|
crop_rect.y,
|
||||||
@@ -520,6 +565,13 @@ void SendImagePopup::render(float time_delta) {
|
|||||||
tmp_img = original_image;
|
tmp_img = original_image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (scale_x != 1.f || scale_y != 1.f) {
|
||||||
|
tmp_img = tmp_img.scale(
|
||||||
|
(int)std::ceil(crop_rect.w*scale_x),
|
||||||
|
(int)std::ceil(crop_rect.h*scale_y)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> new_data;
|
std::vector<uint8_t> new_data;
|
||||||
|
|
||||||
// HACK: generic list
|
// HACK: generic list
|
||||||
|
|||||||
@@ -36,6 +36,10 @@ struct SendImagePopup {
|
|||||||
bool dragging_last_frame_ul {false};
|
bool dragging_last_frame_ul {false};
|
||||||
bool dragging_last_frame_lr {false};
|
bool dragging_last_frame_lr {false};
|
||||||
|
|
||||||
|
float scale_x {1.f};
|
||||||
|
float scale_y {1.f};
|
||||||
|
bool scale_tie {true};
|
||||||
|
|
||||||
// texture to render (orig img)
|
// texture to render (orig img)
|
||||||
TextureEntry preview_image;
|
TextureEntry preview_image;
|
||||||
|
|
||||||
|
|||||||
@@ -27,9 +27,7 @@
|
|||||||
|
|
||||||
#include "./chat_gui/contact_list.hpp"
|
#include "./chat_gui/contact_list.hpp"
|
||||||
|
|
||||||
#include "./media_meta_info_loader.hpp"
|
|
||||||
#include "./sdl_clipboard_utils.hpp"
|
#include "./sdl_clipboard_utils.hpp"
|
||||||
#include "os_comps.hpp"
|
|
||||||
|
|
||||||
#include "./string_formatter_utils.hpp"
|
#include "./string_formatter_utils.hpp"
|
||||||
|
|
||||||
@@ -42,7 +40,7 @@
|
|||||||
#include <iomanip>
|
#include <iomanip>
|
||||||
#include <sstream>
|
#include <sstream>
|
||||||
#include <string>
|
#include <string>
|
||||||
#include <variant>
|
#include <iostream>
|
||||||
|
|
||||||
// TODO: split into msg and c
|
// TODO: split into msg and c
|
||||||
namespace Components {
|
namespace Components {
|
||||||
@@ -1074,7 +1072,7 @@ void ChatGui4::renderChatLog(Contact4 c, bool window_focused, const std::vector<
|
|||||||
ImGui::EndTable();
|
ImGui::EndTable();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ImGui::Shortcut(ImGuiKey_End, ImGuiInputFlags_RouteGlobal)) {
|
if (ImGui::Shortcut(ImGuiKey_G | ImGuiMod_Shift, ImGuiInputFlags_RouteGlobal) || ImGui::Shortcut(ImGuiKey_End, ImGuiInputFlags_RouteGlobal)) {
|
||||||
ImGui::SetScrollHereY(1.f);
|
ImGui::SetScrollHereY(1.f);
|
||||||
manually_scrolled = true;
|
manually_scrolled = true;
|
||||||
}
|
}
|
||||||
@@ -1420,7 +1418,7 @@ void ChatGui4::renderMessageBodyFile(Message3Registry& reg, const Message3 e) {
|
|||||||
if (ImGui::IsItemVisible() && o.all_of<ObjComp::F::TagLocalHaveAll, ObjComp::F::SingleInfo, ObjComp::Ephemeral::BackendFile2>()) {
|
if (ImGui::IsItemVisible() && o.all_of<ObjComp::F::TagLocalHaveAll, ObjComp::F::SingleInfo, ObjComp::Ephemeral::BackendFile2>()) {
|
||||||
ImGui::SetCursorPos(orig_curser_pos); // reset for actual img
|
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 cache gives 0s, fall back to frame dims (eg if pic not loaded yet)
|
||||||
//if (img_width == 0 || img_height == 0) {
|
//if (img_width == 0 || img_height == 0) {
|
||||||
|
|||||||
@@ -1,6 +1,9 @@
|
|||||||
#include "./image_loader.hpp"
|
#include "./image_loader.hpp"
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
#include "./image_scaler.hpp"
|
||||||
|
|
||||||
ImageLoaderI::ImageResult ImageLoaderI::ImageResult::crop(int32_t c_x, int32_t c_y, int32_t c_w, int32_t c_h) const {
|
ImageLoaderI::ImageResult ImageLoaderI::ImageResult::crop(int32_t c_x, int32_t c_y, int32_t c_w, int32_t c_h) const {
|
||||||
// TODO: proper error handling
|
// TODO: proper error handling
|
||||||
@@ -30,3 +33,23 @@ ImageLoaderI::ImageResult ImageLoaderI::ImageResult::crop(int32_t c_x, int32_t c
|
|||||||
return new_image;
|
return new_image;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImageLoaderI::ImageResult ImageLoaderI::ImageResult::scale(int32_t w, int32_t h) const {
|
||||||
|
assert(w > 0);
|
||||||
|
assert(h > 0);
|
||||||
|
|
||||||
|
ImageLoaderI::ImageResult new_image;
|
||||||
|
new_image.width = w;
|
||||||
|
new_image.height = h;
|
||||||
|
new_image.file_ext = file_ext;
|
||||||
|
|
||||||
|
for (const auto& input_frame : frames) {
|
||||||
|
auto& new_frame = new_image.frames.emplace_back();
|
||||||
|
new_frame.ms = input_frame.ms;
|
||||||
|
|
||||||
|
new_frame.data.resize(w*h*4);
|
||||||
|
|
||||||
|
image_scale(new_frame.data.data(), w, h, const_cast<uint8_t*>(input_frame.data.data()), width, height);
|
||||||
|
}
|
||||||
|
|
||||||
|
return new_image;
|
||||||
|
}
|
||||||
|
|||||||
@@ -28,7 +28,8 @@ struct ImageLoaderI {
|
|||||||
// only positive values are valid
|
// only positive values are valid
|
||||||
ImageResult crop(int32_t c_x, int32_t c_y, int32_t c_w, int32_t c_h) const;
|
ImageResult crop(int32_t c_x, int32_t c_y, int32_t c_w, int32_t c_h) const;
|
||||||
|
|
||||||
// TODO: scale
|
// only values > 0 are valid
|
||||||
|
ImageResult scale(int32_t w, int32_t h) const;
|
||||||
};
|
};
|
||||||
virtual ImageResult loadFromMemoryRGBA(const uint8_t* data, uint64_t data_size) = 0;
|
virtual ImageResult loadFromMemoryRGBA(const uint8_t* data, uint64_t data_size) = 0;
|
||||||
};
|
};
|
||||||
|
|||||||
170
src/image_scaler.cpp
Normal file
170
src/image_scaler.cpp
Normal file
@@ -0,0 +1,170 @@
|
|||||||
|
#include "./image_scaler.hpp"
|
||||||
|
|
||||||
|
#include <cmath>
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
// requires ColorTmp to have * and + operators
|
||||||
|
|
||||||
|
struct ColorCanvas8888;
|
||||||
|
|
||||||
|
struct ColorFloat4 {
|
||||||
|
float v[4]{};
|
||||||
|
|
||||||
|
ColorFloat4& operator*=(const float scalar) {
|
||||||
|
v[0] *= scalar;
|
||||||
|
v[1] *= scalar;
|
||||||
|
v[2] *= scalar;
|
||||||
|
v[3] *= scalar;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorFloat4 operator*(const float scalar) const {
|
||||||
|
ColorFloat4 newcf = *this;
|
||||||
|
newcf.v[0] *= scalar;
|
||||||
|
newcf.v[1] *= scalar;
|
||||||
|
newcf.v[2] *= scalar;
|
||||||
|
newcf.v[3] *= scalar;
|
||||||
|
return newcf;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorFloat4& operator/=(const float scalar) {
|
||||||
|
v[0] /= scalar;
|
||||||
|
v[1] /= scalar;
|
||||||
|
v[2] /= scalar;
|
||||||
|
v[3] /= scalar;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorFloat4& operator+=(const ColorFloat4& color) {
|
||||||
|
v[0] += color.v[0];
|
||||||
|
v[1] += color.v[1];
|
||||||
|
v[2] += color.v[2];
|
||||||
|
v[3] += color.v[3];
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
struct ColorCanvas8888 {
|
||||||
|
uint8_t* ptr {nullptr};
|
||||||
|
|
||||||
|
ColorFloat4 operator[](size_t i) const {
|
||||||
|
return {
|
||||||
|
{
|
||||||
|
float(ptr[i*4+0])/255.f,
|
||||||
|
float(ptr[i*4+1])/255.f,
|
||||||
|
float(ptr[i*4+2])/255.f,
|
||||||
|
float(ptr[i*4+3])/255.f,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
void set(size_t i, const ColorFloat4& color) {
|
||||||
|
ptr[i*4+0] = std::round(color.v[0]*255.f);
|
||||||
|
ptr[i*4+1] = std::round(color.v[1]*255.f);
|
||||||
|
ptr[i*4+2] = std::round(color.v[2]*255.f);
|
||||||
|
ptr[i*4+3] = std::round(color.v[3]*255.f);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
template<typename ColorCanvas, typename ColorTmp>
|
||||||
|
constexpr void image_scale(ColorCanvas& dst, const int dst_w, const int dst_h, const ColorCanvas& src, const int src_w, const int src_h) {
|
||||||
|
// Box sampling - Imagine projecting the new, smaller pixels onto the larger source, covering multiple pixel.
|
||||||
|
for (int y = 0; y < dst_h; y++) {
|
||||||
|
for (int x = 0; x < dst_w; x++) {
|
||||||
|
// We perform a weighted mean.
|
||||||
|
ColorTmp color;
|
||||||
|
float weight_sum = 0.f;
|
||||||
|
|
||||||
|
// Walk from upper edge to bottom edge (vertical)
|
||||||
|
const float edge_up = ((float)y * src_h) / dst_h;
|
||||||
|
const float edge_down = ((y + 1.f) * src_h) / dst_h;
|
||||||
|
for (float frac_pos_y = edge_up; frac_pos_y < edge_down;) {
|
||||||
|
const int src_y = (int)std::floor(frac_pos_y); assert(src_y < src_h);
|
||||||
|
const float frac_y = 1.f - (frac_pos_y - src_y);
|
||||||
|
|
||||||
|
// Walk from left edge to right edge (horizontal)
|
||||||
|
const float edge_left = ((float)x * src_w) / dst_w;
|
||||||
|
const float edge_right = ((x + 1.f) * src_w) / dst_w;
|
||||||
|
for (float frac_pos_x = edge_left; frac_pos_x < edge_right;) {
|
||||||
|
const int src_x = (int)std::floor(frac_pos_x); assert(src_x < src_w);
|
||||||
|
const float frac_x = 1.f - (frac_pos_x - src_x);
|
||||||
|
|
||||||
|
const float src_pixel_weight = frac_x * frac_y;
|
||||||
|
|
||||||
|
//const ColorTmp pixel_color = ImGui::ColorConvertU32ToFloat4(src[src_y * src_w + src_x]);
|
||||||
|
const ColorTmp pixel_color = src[src_y * src_w + src_x];
|
||||||
|
color += pixel_color * src_pixel_weight;
|
||||||
|
weight_sum += src_pixel_weight;
|
||||||
|
|
||||||
|
frac_pos_x += frac_x;
|
||||||
|
}
|
||||||
|
|
||||||
|
frac_pos_y += frac_y;
|
||||||
|
}
|
||||||
|
|
||||||
|
color /= weight_sum;
|
||||||
|
|
||||||
|
dst.set(y * dst_w + x, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool image_scale(uint8_t* dst, const int dst_w, const int dst_h, uint8_t* src, const int src_w, const int src_h) {
|
||||||
|
if (dst == nullptr || src == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (dst_w == src_w && dst_h == src_h) {
|
||||||
|
assert(false && "fix me !");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorCanvas8888 dst_c{dst};
|
||||||
|
const ColorCanvas8888 src_c{src};
|
||||||
|
|
||||||
|
image_scale<ColorCanvas8888, ColorFloat4>(
|
||||||
|
dst_c,
|
||||||
|
dst_w, dst_h,
|
||||||
|
src_c,
|
||||||
|
src_w, src_h
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool image_scale(SDL_Surface* dst, SDL_Surface* src) {
|
||||||
|
if (dst == nullptr || src == nullptr) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dst->format != src->format) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: handle other numbers of components beside 4
|
||||||
|
|
||||||
|
if (
|
||||||
|
src->format != SDL_PIXELFORMAT_RGBA8888 &&
|
||||||
|
src->format != SDL_PIXELFORMAT_ARGB8888 &&
|
||||||
|
src->format != SDL_PIXELFORMAT_BGRA8888 &&
|
||||||
|
src->format != SDL_PIXELFORMAT_ABGR8888 &&
|
||||||
|
src->format != SDL_PIXELFORMAT_RGBX8888 &&
|
||||||
|
src->format != SDL_PIXELFORMAT_XRGB8888 &&
|
||||||
|
src->format != SDL_PIXELFORMAT_BGRX8888 &&
|
||||||
|
src->format != SDL_PIXELFORMAT_XBGR8888
|
||||||
|
) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
ColorCanvas8888 dst_c{reinterpret_cast<uint8_t*>(dst->pixels)};
|
||||||
|
const ColorCanvas8888 src_c{reinterpret_cast<uint8_t*>(src->pixels)};
|
||||||
|
|
||||||
|
image_scale<ColorCanvas8888, ColorFloat4>(
|
||||||
|
dst_c,
|
||||||
|
dst->w, dst->h,
|
||||||
|
src_c,
|
||||||
|
src->w, src->h
|
||||||
|
);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
9
src/image_scaler.hpp
Normal file
9
src/image_scaler.hpp
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <SDL3/SDL.h>
|
||||||
|
#include <cstdint>
|
||||||
|
|
||||||
|
bool image_scale(uint8_t* dst, const int dst_w, const int dst_h, uint8_t* src, const int src_w, const int src_h);
|
||||||
|
|
||||||
|
bool image_scale(SDL_Surface* dst, SDL_Surface* src);
|
||||||
|
|
||||||
@@ -8,9 +8,8 @@
|
|||||||
|
|
||||||
#include <solanaceae/message3/components.hpp>
|
#include <solanaceae/message3/components.hpp>
|
||||||
|
|
||||||
#include "./os_comps.hpp"
|
|
||||||
|
|
||||||
#include <solanaceae/object_store/object_store.hpp>
|
#include <solanaceae/object_store/object_store.hpp>
|
||||||
|
#include <solanaceae/object_store/meta_components_file.hpp>
|
||||||
|
|
||||||
#include <solanaceae/file/file2.hpp>
|
#include <solanaceae/file/file2.hpp>
|
||||||
|
|
||||||
@@ -25,7 +24,7 @@ MessageImageLoader::MessageImageLoader(void) {
|
|||||||
_image_loaders.push_back(std::make_unique<ImageLoaderSDLImage>());
|
_image_loaders.push_back(std::make_unique<ImageLoaderSDLImage>());
|
||||||
}
|
}
|
||||||
|
|
||||||
TextureLoaderResult MessageImageLoader::load(TextureUploaderI& tu, Message3Handle m) {
|
TextureLoaderResult MessageImageLoader::load(TextureUploaderI& tu, Message3Handle m, uint32_t w, uint32_t h) {
|
||||||
if (!static_cast<bool>(m)) {
|
if (!static_cast<bool>(m)) {
|
||||||
return {std::nullopt};
|
return {std::nullopt};
|
||||||
}
|
}
|
||||||
@@ -101,6 +100,17 @@ TextureLoaderResult MessageImageLoader::load(TextureUploaderI& tu, Message3Handl
|
|||||||
TextureEntry new_entry;
|
TextureEntry new_entry;
|
||||||
new_entry.timestamp_last_rendered = getTimeMS();
|
new_entry.timestamp_last_rendered = getTimeMS();
|
||||||
new_entry.current_texture = 0;
|
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) {
|
for (const auto& [ms, data] : res.frames) {
|
||||||
const auto n_t = tu.upload(data.data(), res.width, res.height);
|
const auto n_t = tu.upload(data.data(), res.width, res.height);
|
||||||
if (n_t == 0) {
|
if (n_t == 0) {
|
||||||
@@ -114,9 +124,6 @@ TextureLoaderResult MessageImageLoader::load(TextureUploaderI& tu, Message3Handl
|
|||||||
continue;
|
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";
|
std::cout << "MIL: loaded image file o:" << /*file_path*/ entt::to_integral(o.entity()) << "\n";
|
||||||
|
|
||||||
return {new_entry};
|
return {new_entry};
|
||||||
|
|||||||
@@ -5,13 +5,11 @@
|
|||||||
#include "./image_loader.hpp"
|
#include "./image_loader.hpp"
|
||||||
#include "./texture_cache.hpp"
|
#include "./texture_cache.hpp"
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
class MessageImageLoader {
|
class MessageImageLoader {
|
||||||
std::vector<std::unique_ptr<ImageLoaderI>> _image_loaders;
|
std::vector<std::unique_ptr<ImageLoaderI>> _image_loaders;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
MessageImageLoader(void);
|
MessageImageLoader(void);
|
||||||
TextureLoaderResult load(TextureUploaderI& tu, Message3Handle m);
|
TextureLoaderResult load(TextureUploaderI& tu, Message3Handle m, uint32_t w, uint32_t h);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -51,8 +51,11 @@ TextureEntry generateTestAnim(TextureUploaderI& tu) {
|
|||||||
new_entry.textures.emplace_back(n_t);
|
new_entry.textures.emplace_back(n_t);
|
||||||
new_entry.frame_duration.emplace_back(250);
|
new_entry.frame_duration.emplace_back(250);
|
||||||
}
|
}
|
||||||
|
// TODO: 2x2?
|
||||||
new_entry.width = 0;
|
new_entry.width = 0;
|
||||||
new_entry.height = 0;
|
new_entry.height = 0;
|
||||||
|
new_entry.src_width = 0;
|
||||||
|
new_entry.src_height = 0;
|
||||||
return new_entry;
|
return new_entry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,6 +16,10 @@
|
|||||||
struct TextureEntry {
|
struct TextureEntry {
|
||||||
uint32_t width {0};
|
uint32_t width {0};
|
||||||
uint32_t height {0};
|
uint32_t height {0};
|
||||||
|
|
||||||
|
uint32_t src_width {0};
|
||||||
|
uint32_t src_height {0};
|
||||||
|
|
||||||
std::vector<uint64_t> textures;
|
std::vector<uint64_t> textures;
|
||||||
std::vector<uint32_t> frame_duration; // ms
|
std::vector<uint32_t> frame_duration; // ms
|
||||||
size_t current_texture {0};
|
size_t current_texture {0};
|
||||||
@@ -28,6 +32,8 @@ struct TextureEntry {
|
|||||||
TextureEntry(const TextureEntry& other) :
|
TextureEntry(const TextureEntry& other) :
|
||||||
width(other.width),
|
width(other.width),
|
||||||
height(other.height),
|
height(other.height),
|
||||||
|
src_width(other.src_width),
|
||||||
|
src_height(other.src_height),
|
||||||
textures(other.textures),
|
textures(other.textures),
|
||||||
frame_duration(other.frame_duration),
|
frame_duration(other.frame_duration),
|
||||||
current_texture(other.current_texture),
|
current_texture(other.current_texture),
|
||||||
@@ -39,6 +45,8 @@ struct TextureEntry {
|
|||||||
TextureEntry& operator=(const TextureEntry& other) {
|
TextureEntry& operator=(const TextureEntry& other) {
|
||||||
width = other.width;
|
width = other.width;
|
||||||
height = other.height;
|
height = other.height;
|
||||||
|
src_width = other.src_width;
|
||||||
|
src_height = other.src_height;
|
||||||
textures = other.textures;
|
textures = other.textures;
|
||||||
frame_duration = other.frame_duration;
|
frame_duration = other.frame_duration;
|
||||||
current_texture = other.current_texture;
|
current_texture = other.current_texture;
|
||||||
@@ -97,7 +105,11 @@ struct TextureCache {
|
|||||||
TextureEntry _default_texture;
|
TextureEntry _default_texture;
|
||||||
|
|
||||||
entt::dense_map<KeyType, TextureEntry> _cache;
|
entt::dense_map<KeyType, TextureEntry> _cache;
|
||||||
entt::dense_set<KeyType> _to_load;
|
struct LoadDims {
|
||||||
|
uint32_t w{0};
|
||||||
|
uint32_t h{0};
|
||||||
|
};
|
||||||
|
entt::dense_map<KeyType, LoadDims> _to_load;
|
||||||
// to_reload // to_update? _marked_stale?
|
// to_reload // to_update? _marked_stale?
|
||||||
|
|
||||||
const uint64_t ms_before_purge {60 * 1000ull};
|
const uint64_t ms_before_purge {60 * 1000ull};
|
||||||
@@ -126,21 +138,37 @@ struct TextureCache {
|
|||||||
uint32_t width;
|
uint32_t width;
|
||||||
uint32_t height;
|
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);
|
auto it = _cache.find(key);
|
||||||
|
|
||||||
if (it != _cache.end()) {
|
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 {
|
return {
|
||||||
it->second.template getID<TextureType>(),
|
it->second.template getID<TextureType>(),
|
||||||
it->second.width,
|
it->second.src_width,
|
||||||
it->second.height
|
it->second.src_height
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
_to_load.insert(key);
|
// TODO: only overwrite smaller dims (or combine max)
|
||||||
|
_to_load.insert({key, LoadDims{width, height}});
|
||||||
|
|
||||||
|
// return fallback
|
||||||
return {
|
return {
|
||||||
_default_texture.getID<TextureType>(),
|
_default_texture.getID<TextureType>(),
|
||||||
_default_texture.width,
|
_default_texture.src_width,
|
||||||
_default_texture.height
|
_default_texture.src_height
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -153,7 +181,7 @@ struct TextureCache {
|
|||||||
if (it == _cache.end()) {
|
if (it == _cache.end()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
_to_load.insert(key);
|
_to_load.insert({key, {it->second.width, it->second.height}});
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -202,17 +230,18 @@ struct TextureCache {
|
|||||||
bool workLoadQueue(void) {
|
bool workLoadQueue(void) {
|
||||||
auto it = _to_load.cbegin();
|
auto it = _to_load.cbegin();
|
||||||
for (; it != _to_load.cend(); it++) {
|
for (; it != _to_load.cend(); it++) {
|
||||||
auto new_entry_opt = _l.load(_tu, *it);
|
const auto& load_key = it->first;
|
||||||
if (_cache.count(*it)) {
|
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()) {
|
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());
|
assert(!old_entry.textures.empty());
|
||||||
for (const auto& tex_id : old_entry.textures) {
|
for (const auto& tex_id : old_entry.textures) {
|
||||||
_tu.destroy(tex_id);
|
_tu.destroy(tex_id);
|
||||||
}
|
}
|
||||||
|
|
||||||
_cache.erase(*it);
|
_cache.erase(load_key);
|
||||||
auto& new_entry = _cache[*it] = new_entry_opt.texture.value();
|
auto& new_entry = _cache[load_key] = new_entry_opt.texture.value();
|
||||||
// TODO: make update interface and let loader handle this
|
// TODO: make update interface and let loader handle this
|
||||||
//new_entry.current_texture = old_entry.current_texture; // ??
|
//new_entry.current_texture = old_entry.current_texture; // ??
|
||||||
new_entry.rendered_this_frame = old_entry.rendered_this_frame;
|
new_entry.rendered_this_frame = old_entry.rendered_this_frame;
|
||||||
@@ -228,8 +257,8 @@ struct TextureCache {
|
|||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (new_entry_opt.texture.has_value()) {
|
if (new_entry_opt.texture.has_value()) {
|
||||||
_cache.emplace(*it, new_entry_opt.texture.value());
|
_cache.emplace(load_key, new_entry_opt.texture.value());
|
||||||
_cache.at(*it).rendered_this_frame = true; // ?
|
_cache.at(load_key).rendered_this_frame = true; // ?
|
||||||
it = _to_load.erase(it);
|
it = _to_load.erase(it);
|
||||||
|
|
||||||
// TODO: not a good idea?
|
// TODO: not a good idea?
|
||||||
|
|||||||
@@ -195,7 +195,7 @@ static std::vector<uint8_t> generateToxIdenticon(const ToxKey& key) {
|
|||||||
return pixels;
|
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();
|
const auto& cr = _cs.registry();
|
||||||
if (!cr.valid(c)) {
|
if (!cr.valid(c)) {
|
||||||
return {std::nullopt};
|
return {std::nullopt};
|
||||||
@@ -234,14 +234,23 @@ TextureLoaderResult ToxAvatarLoader::load(TextureUploaderI& tu, Contact4 c) {
|
|||||||
TextureEntry new_entry;
|
TextureEntry new_entry;
|
||||||
new_entry.timestamp_last_rendered = getTimeMS();
|
new_entry.timestamp_last_rendered = getTimeMS();
|
||||||
new_entry.current_texture = 0;
|
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) {
|
for (const auto& [ms, data] : res.frames) {
|
||||||
const auto n_t = tu.upload(data.data(), res.width, res.height);
|
const auto n_t = tu.upload(data.data(), res.width, res.height);
|
||||||
new_entry.textures.push_back(n_t);
|
new_entry.textures.push_back(n_t);
|
||||||
new_entry.frame_duration.push_back(ms);
|
new_entry.frame_duration.push_back(ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
new_entry.width = res.width;
|
|
||||||
new_entry.height = res.height;
|
|
||||||
|
|
||||||
if (cr.all_of<Contact::Components::AvatarFile>(c)) {
|
if (cr.all_of<Contact::Components::AvatarFile>(c)) {
|
||||||
std::cout << "TAL: loaded image file " << cr.get<Contact::Components::AvatarFile>(c).file_path << "\n";
|
std::cout << "TAL: loaded image file " << cr.get<Contact::Components::AvatarFile>(c).file_path << "\n";
|
||||||
|
|||||||
@@ -8,8 +8,6 @@
|
|||||||
#include "./image_loader.hpp"
|
#include "./image_loader.hpp"
|
||||||
#include "./texture_cache.hpp"
|
#include "./texture_cache.hpp"
|
||||||
|
|
||||||
#include <optional>
|
|
||||||
|
|
||||||
class ToxAvatarLoader {
|
class ToxAvatarLoader {
|
||||||
ContactStore4I& _cs;
|
ContactStore4I& _cs;
|
||||||
ObjectStore2& _os;
|
ObjectStore2& _os;
|
||||||
@@ -21,6 +19,6 @@ class ToxAvatarLoader {
|
|||||||
|
|
||||||
public:
|
public:
|
||||||
ToxAvatarLoader(ContactStore4I& cs, ObjectStore2& os);
|
ToxAvatarLoader(ContactStore4I& cs, ObjectStore2& os);
|
||||||
TextureLoaderResult load(TextureUploaderI& tu, Contact4 c);
|
TextureLoaderResult load(TextureUploaderI& tu, Contact4 c, uint32_t w, uint32_t h);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -79,18 +79,48 @@ void ToxUIUtils::render(void) {
|
|||||||
_tc.runBootstrap();
|
_tc.runBootstrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (ImGui::MenuItem("connect node", nullptr, _show_dht_connect_node)) {
|
||||||
|
_show_dht_connect_node = !_show_dht_connect_node;
|
||||||
|
}
|
||||||
|
|
||||||
|
{ // self node info
|
||||||
|
bool either = false;
|
||||||
|
{
|
||||||
|
auto [port_opt, port_err] = _tc.toxSelfGetUDPPort();
|
||||||
|
if (port_opt.has_value()) {
|
||||||
|
either = true;
|
||||||
|
ImGui::TextDisabled("udp online; port: %d", port_opt.value());
|
||||||
|
} else {
|
||||||
|
ImGui::TextDisabled("udp disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
auto [port_opt, port_err] = _tc.toxSelfGetTCPPort();
|
||||||
|
if (port_opt.has_value()) {
|
||||||
|
either = true;
|
||||||
|
ImGui::TextDisabled("tcp relay server online; port: %d", port_opt.value());
|
||||||
|
} else {
|
||||||
|
ImGui::TextDisabled("tcp relay server disabled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (either && ImGui::MenuItem("copy own DHT id/pubkey")) {
|
||||||
|
ImGui::SetClipboardText(bin2hex(_tc.toxSelfGetDHTID()).c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
|
|
||||||
switch (_tc.toxSelfGetConnectionStatus()) {
|
switch (_tc.toxSelfGetConnectionStatus()) {
|
||||||
case TOX_CONNECTION_NONE:
|
case TOX_CONNECTION_NONE:
|
||||||
ImGui::TextColored({1.0,0.5,0.5,0.8}, "Offline");
|
ImGui::TextColored({1.0,0.5,0.5,0.8}, "Disconnected");
|
||||||
break;
|
break;
|
||||||
case TOX_CONNECTION_TCP:
|
case TOX_CONNECTION_TCP:
|
||||||
ImGui::TextColored({0.0,1.0,0.8,0.8}, "Online-TCP");
|
ImGui::TextColored({0.0,1.0,0.8,0.8}, "Connected-TCP");
|
||||||
break;
|
break;
|
||||||
case TOX_CONNECTION_UDP:
|
case TOX_CONNECTION_UDP:
|
||||||
ImGui::TextColored({0.3,1.0,0.0,0.8}, "Online-UDP");
|
ImGui::TextColored({0.3,1.0,0.0,0.8}, "Connected-UDP");
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -236,5 +266,81 @@ void ToxUIUtils::render(void) {
|
|||||||
}
|
}
|
||||||
ImGui::End();
|
ImGui::End();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (_show_dht_connect_node) {
|
||||||
|
if (ImGui::Begin("Tox connect DHT node", &_show_dht_connect_node)) {
|
||||||
|
ImGui::BeginDisabled();
|
||||||
|
ImGui::TextWrapped(
|
||||||
|
"Here you can manually connect to a DHT node (or/and tcp-relay) by address and id/pubkey.\n"
|
||||||
|
"This is equivalent to what 'DHT Bootstrapping' does, but not with hardcoded nodes.\n"
|
||||||
|
"Keep in mind that your own DHT id/pubkey changes everytime you start the program, unlike dedicated bootstrap nodes.\n"
|
||||||
|
"If DNS querries where not disabled at launch, domain names can be used too."
|
||||||
|
"A public list can be found at: nodes.tox.chat\n"
|
||||||
|
);
|
||||||
|
ImGui::EndDisabled();
|
||||||
|
|
||||||
|
static std::string addr;
|
||||||
|
static uint16_t port {33445};
|
||||||
|
static char pubkey[TOX_PUBLIC_KEY_SIZE*2 + 1]; // 1 for null terminator
|
||||||
|
|
||||||
|
if (ImGui::BeginTable("node", 2, ImGuiTableFlags_SizingFixedFit)) {
|
||||||
|
ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthFixed);
|
||||||
|
ImGui::TableSetupColumn(nullptr, ImGuiTableColumnFlags_WidthStretch);
|
||||||
|
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::TextUnformatted("address");
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::SetNextItemWidth(-1);
|
||||||
|
ImGui::InputText("##address", &addr);
|
||||||
|
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::TextUnformatted("port");
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::SetNextItemWidth(-1);
|
||||||
|
ImGui::InputScalar("##port", ImGuiDataType_U16, &port);
|
||||||
|
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::TextUnformatted("pubkey");
|
||||||
|
ImGui::TableNextColumn();
|
||||||
|
ImGui::SetNextItemWidth(-1);
|
||||||
|
ImGui::InputText("##pubkey", pubkey, TOX_PUBLIC_KEY_SIZE*2+1);
|
||||||
|
|
||||||
|
// add as
|
||||||
|
// - udp dht node (default)
|
||||||
|
// - udp dht node + tcp relay
|
||||||
|
// - tcp relay
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
|
||||||
|
static std::string last_error;
|
||||||
|
|
||||||
|
bool valid_input = !addr.empty() && port != 0;
|
||||||
|
if (!valid_input) ImGui::BeginDisabled();
|
||||||
|
if (ImGui::Button("connect")) {
|
||||||
|
std::vector<uint8_t> bin_pubkey = hex2bin(std::string_view{pubkey, TOX_PUBLIC_KEY_SIZE*2});
|
||||||
|
|
||||||
|
last_error.clear();
|
||||||
|
|
||||||
|
{
|
||||||
|
Tox_Err_Bootstrap err = _tc.toxBootstrap(addr, port, bin_pubkey);
|
||||||
|
if (err != Tox_Err_Bootstrap::TOX_ERR_BOOTSTRAP_OK) {
|
||||||
|
last_error += "add udp node failed with " + std::to_string(err) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
{
|
||||||
|
Tox_Err_Bootstrap err = _tc.toxAddTcpRelay(addr, port, bin_pubkey);
|
||||||
|
if (err != Tox_Err_Bootstrap::TOX_ERR_BOOTSTRAP_OK) {
|
||||||
|
last_error += "add tcp relay failed with " + std::to_string(err) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!valid_input) ImGui::EndDisabled();
|
||||||
|
if (!last_error.empty()) {
|
||||||
|
ImGui::TextUnformatted(last_error.c_str());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class ToxUIUtils {
|
|||||||
bool _show_add_friend_window {false};
|
bool _show_add_friend_window {false};
|
||||||
bool _show_add_group_window {false};
|
bool _show_add_group_window {false};
|
||||||
bool _show_new_group_window {false};
|
bool _show_new_group_window {false};
|
||||||
|
bool _show_dht_connect_node {false};
|
||||||
|
|
||||||
ToxClient& _tc;
|
ToxClient& _tc;
|
||||||
ToxContactModel2& _tcm;
|
ToxContactModel2& _tcm;
|
||||||
|
|||||||
Reference in New Issue
Block a user