diff --git a/src/chat_gui/send_image_popup.cpp b/src/chat_gui/send_image_popup.cpp index d805b01..f4466ae 100644 --- a/src/chat_gui/send_image_popup.cpp +++ b/src/chat_gui/send_image_popup.cpp @@ -13,6 +13,7 @@ #include +#include #include SendImagePopup::SendImagePopup(TextureUploaderI& tu) : _tu(tu) { @@ -245,7 +246,7 @@ void SendImagePopup::render(float time_delta) { - ( ImGui::GetWindowContentRegionMin().y + TEXT_BASE_HEIGHT*(2-1) // row of buttons (-1 bc fh inclues fontsize) - + ImGui::GetFrameHeightWithSpacing()*4 + + ImGui::GetFrameHeightWithSpacing()*6 ) ; if (height > max_height) { @@ -340,23 +341,22 @@ void SendImagePopup::render(float time_delta) { { // 4 lines delimiting the crop result ImU32 line_color = 0xffffffff; { // calc color + static constexpr auto f = [](float x) { + while (x < 0.f) { + x += 1.f; + } + + x = std::fmod(x, 1.f); // fract() + + if (x < 1.f/3) { + return x * 3; + } else if (x < 2.f/3) { + return (1 - (x - (1.f/3))) * 3 - 2; + } else { + return 0.f; + } + }; auto rgb = [](float x) -> ImVec4 { - auto f = [](float x) { - while (x < 0.f) { - x += 1.f; - } - - x = std::fmod(x, 1.f); // fract() - - if (x < 1.f/3) { - return x * 3; - } else if (x < 2.f/3) { - return (1 - (x - (1.f/3))) * 3 - 2; - } else { - return 0.f; - } - }; - float red = f(x); float green = f(x - (1.f/3)); float blue = f(x - (2.f/3)); @@ -434,7 +434,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 (ImGui::Button("done")) { cropping = false; @@ -445,7 +445,7 @@ void SendImagePopup::render(float time_delta) { } } ImGui::SameLine(); - if (ImGui::Button("reset")) { + if (ImGui::Button("reset##crop")) { crop_rect.x = 0; crop_rect.y = 0; crop_rect.w = original_image.width; @@ -455,6 +455,35 @@ void SendImagePopup::render(float time_delta) { 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::TextUnformatted("scale"); + ImGui::SameLine(); + if (ImGui::Button("reset##scale")) { + scale_x = 1.f; + scale_y = 1.f; + compress = false; // feels nice + } + 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(); + 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; if (cropped) { if (!compress) { @@ -463,6 +492,13 @@ void SendImagePopup::render(float time_delta) { } 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); if (cropped && ImGui::IsItemHovered()) { @@ -505,11 +541,10 @@ void SendImagePopup::render(float time_delta) { } ImGui::SameLine(); 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 ImageLoaderI::ImageResult tmp_img; if (cropped) { - std::cout << "SIP: CROP!!!!!\n"; tmp_img = original_image.crop( crop_rect.x, crop_rect.y, @@ -520,6 +555,15 @@ void SendImagePopup::render(float time_delta) { 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) + ); + } else { + tmp_img = original_image; + } + std::vector new_data; // HACK: generic list diff --git a/src/chat_gui/send_image_popup.hpp b/src/chat_gui/send_image_popup.hpp index 9a88d2c..4427f31 100644 --- a/src/chat_gui/send_image_popup.hpp +++ b/src/chat_gui/send_image_popup.hpp @@ -36,6 +36,10 @@ struct SendImagePopup { bool dragging_last_frame_ul {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) TextureEntry preview_image; diff --git a/src/image_loader.cpp b/src/image_loader.cpp index 5fe0863..55c7bd0 100644 --- a/src/image_loader.cpp +++ b/src/image_loader.cpp @@ -1,6 +1,9 @@ #include "./image_loader.hpp" #include +#include + +#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 { // TODO: proper error handling @@ -30,3 +33,23 @@ ImageLoaderI::ImageResult ImageLoaderI::ImageResult::crop(int32_t c_x, int32_t c 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(input_frame.data.data()), width, height); + } + + return new_image; +} diff --git a/src/image_loader.hpp b/src/image_loader.hpp index 522ce53..2eef251 100644 --- a/src/image_loader.hpp +++ b/src/image_loader.hpp @@ -28,7 +28,8 @@ struct ImageLoaderI { // only positive values are valid 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; }; diff --git a/src/image_scaler.cpp b/src/image_scaler.cpp index 4a984c8..ce0b322 100644 --- a/src/image_scaler.cpp +++ b/src/image_scaler.cpp @@ -67,7 +67,7 @@ struct ColorCanvas8888 { }; template -void image_scale(ColorCanvas& dst, const int dst_w, const int dst_h, const ColorCanvas& src, const int src_w, const int src_h) { +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++) { @@ -109,7 +109,33 @@ void image_scale(ColorCanvas& dst, const int dst_w, const int dst_h, const 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( + 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; } diff --git a/src/image_scaler.hpp b/src/image_scaler.hpp index 755a94b..a9ee38d 100644 --- a/src/image_scaler.hpp +++ b/src/image_scaler.hpp @@ -1,6 +1,9 @@ #pragma once #include +#include + +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);