forked from Green-Sky/tomato
hack in compressor types for sip + a bunch of refactoring and fixes
This commit is contained in:
parent
62b00a4bd6
commit
c24dc45e93
@ -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
32
src/image_loader.cpp
Normal 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;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
@ -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 {};
|
||||||
}
|
}
|
||||||
|
@ -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", ¤t_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
|
||||||
const uint32_t qmin = 1;
|
if (current_compressor == 0 || current_compressor == 1) {
|
||||||
const uint32_t qmax = 100;
|
const uint32_t qmin = 1;
|
||||||
recalc_size |= ImGui::SliderScalar("quality", ImGuiDataType_U32, &quality, &qmin, &qmax);
|
const uint32_t qmax = 100;
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!new_data.empty()) {
|
std::vector<uint8_t> new_data;
|
||||||
_on_send(new_data, ".webp");
|
|
||||||
|
// HACK: generic list
|
||||||
|
if (current_compressor == 0) {
|
||||||
|
new_data = ImageEncoderWebP{}.encodeToMemoryRGBA(tmp_img, {{"quality", quality}});
|
||||||
|
if (!new_data.empty()) {
|
||||||
|
_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);
|
||||||
}
|
}
|
||||||
|
@ -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:
|
||||||
|
Loading…
Reference in New Issue
Block a user