hack in compressor types for sip + a bunch of refactoring and fixes

This commit is contained in:
Green Sky 2023-10-09 21:17:00 +02:00
parent 62b00a4bd6
commit c24dc45e93
No known key found for this signature in database
6 changed files with 94 additions and 49 deletions

View File

@ -18,6 +18,7 @@ add_executable(tomato
./theme.hpp ./theme.hpp
./image_loader.hpp ./image_loader.hpp
./image_loader.cpp
./image_loader_sdl_bmp.hpp ./image_loader_sdl_bmp.hpp
./image_loader_sdl_bmp.cpp ./image_loader_sdl_bmp.cpp
./image_loader_stb.hpp ./image_loader_stb.hpp

32
src/image_loader.cpp Normal file
View File

@ -0,0 +1,32 @@
#include "./image_loader.hpp"
#include <cassert>
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
assert(c_x+c_w <= width);
assert(c_y+c_h <= height);
ImageLoaderI::ImageResult new_image;
new_image.width = c_w;
new_image.height = c_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;
// TODO: improve this, this is super inefficent
for (int64_t y = c_y; y < c_y + c_h; y++) {
for (int64_t x = c_x; x < c_x + c_w; x++) {
new_frame.data.push_back(input_frame.data.at(y*width*4+x*4+0));
new_frame.data.push_back(input_frame.data.at(y*width*4+x*4+1));
new_frame.data.push_back(input_frame.data.at(y*width*4+x*4+2));
new_frame.data.push_back(input_frame.data.at(y*width*4+x*4+3));
}
}
}
return new_image;
}

View File

@ -8,14 +8,14 @@
struct ImageLoaderI { struct ImageLoaderI {
virtual ~ImageLoaderI(void) {} virtual ~ImageLoaderI(void) {}
struct ImageInfo { struct ImageInfo final {
uint32_t width {0}; uint32_t width {0};
uint32_t height {0}; uint32_t height {0};
const char* file_ext {nullptr}; const char* file_ext {nullptr};
}; };
virtual ImageInfo loadInfoFromMemory(const uint8_t* data, uint64_t data_size) = 0; virtual ImageInfo loadInfoFromMemory(const uint8_t* data, uint64_t data_size) = 0;
struct ImageResult { struct ImageResult final {
uint32_t width {0}; uint32_t width {0};
uint32_t height {0}; uint32_t height {0};
struct Frame { struct Frame {
@ -24,6 +24,9 @@ struct ImageLoaderI {
}; };
std::vector<Frame> frames; std::vector<Frame> frames;
const char* file_ext {nullptr}; const char* file_ext {nullptr};
// only positive values are valid
ImageResult crop(int32_t c_x, int32_t c_y, int32_t c_w, int32_t c_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;
}; };

View File

@ -105,7 +105,7 @@ std::vector<uint8_t> ImageEncoderSTBPNG::encodeToMemoryRGBA(const ImageResult& i
return context.new_data; return context.new_data;
} }
std::vector<uint8_t> ImageEncoderSTBJpeg::encodeToMemoryRGBA(const ImageResult& input_image, const std::map<std::string, float>&) { std::vector<uint8_t> ImageEncoderSTBJpeg::encodeToMemoryRGBA(const ImageResult& input_image, const std::map<std::string, float>& extra_options) {
if (input_image.frames.empty()) { if (input_image.frames.empty()) {
std::cerr << "IESTBJpeg error: empty image\n"; std::cerr << "IESTBJpeg error: empty image\n";
return {}; return {};
@ -116,6 +116,13 @@ std::vector<uint8_t> ImageEncoderSTBJpeg::encodeToMemoryRGBA(const ImageResult&
return {}; return {};
} }
// setup options
float quality = 80.f;
if (extra_options.count("quality")) {
quality = extra_options.at("quality");
}
struct Context { struct Context {
std::vector<uint8_t> new_data; std::vector<uint8_t> new_data;
} context; } context;
@ -125,7 +132,7 @@ std::vector<uint8_t> ImageEncoderSTBJpeg::encodeToMemoryRGBA(const ImageResult&
ctx->new_data.insert(ctx->new_data.cend(), d, d + size); ctx->new_data.insert(ctx->new_data.cend(), d, d + size);
}; };
if (!stbi_write_jpg_to_func(write_f, &context, input_image.width, input_image.height, 4, input_image.frames.front().data.data(), 4*input_image.width)) { if (!stbi_write_jpg_to_func(write_f, &context, input_image.width, input_image.height, 4, input_image.frames.front().data.data(), quality)) {
std::cerr << "IESTBJpeg error: stbi_write_jpg failed!\n"; std::cerr << "IESTBJpeg error: stbi_write_jpg failed!\n";
return {}; return {};
} }

View File

@ -100,40 +100,6 @@ bool SendImagePopup::load(void) {
return false; return false;
} }
std::vector<uint8_t> SendImagePopup::compressWebp(const ImageLoaderI::ImageResult& input_image, uint32_t quality) {
// HACK: generic list
return ImageEncoderWebP{}.encodeToMemoryRGBA(input_image, {{"quality", quality}});
}
ImageLoaderI::ImageResult SendImagePopup::crop(const ImageLoaderI::ImageResult& input_image, const Rect& crop_rect) {
// TODO: proper error handling
assert(crop_rect.x+crop_rect.w <= input_image.width);
assert(crop_rect.y+crop_rect.h <= input_image.height);
ImageLoaderI::ImageResult new_image;
new_image.width = crop_rect.w;
new_image.height = crop_rect.h;
new_image.file_ext = input_image.file_ext;
for (const auto& input_frame : input_image.frames) {
auto& new_frame = new_image.frames.emplace_back();
new_frame.ms = input_frame.ms;
// TODO: improve this, this is super inefficent
for (int64_t y = crop_rect.y; y < crop_rect.y + crop_rect.h; y++) {
for (int64_t x = crop_rect.x; x < crop_rect.x + crop_rect.w; x++) {
new_frame.data.push_back(input_frame.data.at(y*input_image.width*4+x*4+0));
new_frame.data.push_back(input_frame.data.at(y*input_image.width*4+x*4+1));
new_frame.data.push_back(input_frame.data.at(y*input_image.width*4+x*4+2));
new_frame.data.push_back(input_frame.data.at(y*input_image.width*4+x*4+3));
}
}
}
return new_image;
}
SendImagePopup::Rect SendImagePopup::sanitizeCrop(Rect crop_rect, int32_t image_width, int32_t image_height) { SendImagePopup::Rect SendImagePopup::sanitizeCrop(Rect crop_rect, int32_t image_width, int32_t image_height) {
// w and h min is 1 -> x/y need to be smaller so the img is atleast 1px in any dim // w and h min is 1 -> x/y need to be smaller so the img is atleast 1px in any dim
if (crop_rect.x >= image_width-1) { if (crop_rect.x >= image_width-1) {
@ -441,18 +407,30 @@ void SendImagePopup::render(void) {
compress = true; compress = true;
} }
recalc_size |= ImGui::Checkbox("reencode", &compress); recalc_size |= ImGui::Checkbox("compress", &compress);
if (cropped && ImGui::IsItemHovered()) { if (cropped && ImGui::IsItemHovered()) {
ImGui::SetTooltip("required since cropped!"); ImGui::SetTooltip("required since cropped!");
} }
static int current_compressor = 0;
if (compress) { if (compress) {
ImGui::SameLine();
ImGui::Combo("##compression_type", &current_compressor, "webp\0jpeg\0png\n");
ImGui::Indent(); ImGui::Indent();
// combo "webp""webp-lossless""png""jpg?" // combo "webp""webp-lossless""png""jpg?"
// if lossy quality slider (1-100) default 80 // if lossy quality slider (1-100) default 80
if (current_compressor == 0 || current_compressor == 1) {
const uint32_t qmin = 1; const uint32_t qmin = 1;
const uint32_t qmax = 100; const uint32_t qmax = 100;
recalc_size |= ImGui::SliderScalar("quality", ImGuiDataType_U32, &quality, &qmin, &qmax); recalc_size |= ImGui::SliderScalar("quality", ImGuiDataType_U32, &quality, &qmin, &qmax);
}
if (current_compressor == 2) {
const uint32_t qmin = 0;
const uint32_t qmax = 9;
recalc_size |= ImGui::SliderScalar("compression_level", ImGuiDataType_U32, &compression_level, &qmin, &qmax);
}
if (recalc_size) { if (recalc_size) {
// compress and save temp? cooldown? async? save size only? // compress and save temp? cooldown? async? save size only?
@ -470,17 +448,41 @@ void SendImagePopup::render(void) {
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) {
std::vector<uint8_t> new_data; // TODO: copy bad
ImageLoaderI::ImageResult tmp_img;
if (cropped) { if (cropped) {
std::cout << "SIP: CROP!!!!!\n"; std::cout << "SIP: CROP!!!!!\n";
new_data = compressWebp(crop(original_image, crop_rect), quality); tmp_img = original_image.crop(
crop_rect.x,
crop_rect.y,
crop_rect.w,
crop_rect.h
);
} else { } else {
new_data = compressWebp(original_image, quality); tmp_img = original_image;
} }
std::vector<uint8_t> new_data;
// HACK: generic list
if (current_compressor == 0) {
new_data = ImageEncoderWebP{}.encodeToMemoryRGBA(tmp_img, {{"quality", quality}});
if (!new_data.empty()) { if (!new_data.empty()) {
_on_send(new_data, ".webp"); _on_send(new_data, ".webp");
} }
} else if (current_compressor == 1) {
new_data = ImageEncoderSTBJpeg{}.encodeToMemoryRGBA(tmp_img, {{"quality", quality}});;
if (!new_data.empty()) {
_on_send(new_data, ".jpg");
}
} else if (current_compressor == 2) {
new_data = ImageEncoderSTBPNG{}.encodeToMemoryRGBA(tmp_img, {{"png_compression_level", compression_level}});;
if (!new_data.empty()) {
_on_send(new_data, ".png");
}
}
// error
} else { } else {
_on_send(original_data, original_file_ext); _on_send(original_data, original_file_ext);
} }

View File

@ -13,6 +13,7 @@ struct SendImagePopup {
// private // private
std::vector<std::unique_ptr<ImageLoaderI>> _image_loaders; std::vector<std::unique_ptr<ImageLoaderI>> _image_loaders;
std::vector<std::unique_ptr<ImageEncoderI>> _image_encoders;
// copy of the original data, dont touch! // copy of the original data, dont touch!
std::vector<uint8_t> original_data; std::vector<uint8_t> original_data;
@ -40,6 +41,7 @@ struct SendImagePopup {
bool compress {false}; bool compress {false};
uint32_t quality {80u}; uint32_t quality {80u};
uint32_t compression_level {8u};
float time {0.f}; // cycling form 0 to 1 over time float time {0.f}; // cycling form 0 to 1 over time
@ -55,8 +57,6 @@ struct SendImagePopup {
// returns if loaded successfully // returns if loaded successfully
bool load(void); bool load(void);
static std::vector<uint8_t> compressWebp(const ImageLoaderI::ImageResult& input_image, uint32_t quality = 80u);
static ImageLoaderI::ImageResult crop(const ImageLoaderI::ImageResult& input_image, const Rect& crop_rect);
static Rect sanitizeCrop(Rect crop_rect, int32_t image_width, int32_t image_height); static Rect sanitizeCrop(Rect crop_rect, int32_t image_width, int32_t image_height);
public: public: