full on qoi

This commit is contained in:
Green Sky 2024-03-04 13:38:55 +01:00
parent 2cbda6e7be
commit 8d5b619884
No known key found for this signature in database
8 changed files with 122 additions and 1 deletions

View File

@ -25,6 +25,8 @@ add_executable(tomato
./image_loader_stb.cpp ./image_loader_stb.cpp
./image_loader_webp.hpp ./image_loader_webp.hpp
./image_loader_webp.cpp ./image_loader_webp.cpp
./image_loader_qoi.hpp
./image_loader_qoi.cpp
./texture_uploader.hpp ./texture_uploader.hpp
./sdlrenderer_texture_uploader.hpp ./sdlrenderer_texture_uploader.hpp
@ -90,5 +92,6 @@ target_link_libraries(tomato PUBLIC
stb_image_write stb_image_write
webpdemux webpdemux
libwebpmux # the f why (needed for anim encode) libwebpmux # the f why (needed for anim encode)
qoi
) )

View File

@ -613,6 +613,7 @@ float ChatGui4::render(float time_delta) {
"image/gif", "image/gif",
"image/jpeg", "image/jpeg",
"image/bmp", "image/bmp",
"image/qoi",
}; };
for (const char* mime_type : image_mime_types) { for (const char* mime_type : image_mime_types) {

91
src/image_loader_qoi.cpp Normal file
View File

@ -0,0 +1,91 @@
#include "./image_loader_qoi.hpp"
#include <cstdint>
#include <qoi/qoi.h>
#include <iostream>
ImageLoaderQOI::ImageInfo ImageLoaderQOI::loadInfoFromMemory(const uint8_t* data, uint64_t data_size) {
ImageInfo res;
qoi_desc desc;
// TODO: only read the header
auto* ret = qoi_decode(data, data_size, &desc, 4);
if (ret == nullptr) {
return res;
}
free(ret);
res.width = desc.width;
res.height = desc.height;
//desc.colorspace;
return res;
}
ImageLoaderQOI::ImageResult ImageLoaderQOI::loadFromMemoryRGBA(const uint8_t* data, uint64_t data_size) {
ImageResult res;
qoi_desc desc;
uint8_t* img_data = static_cast<uint8_t*>(
qoi_decode(data, data_size, &desc, 4)
);
if (img_data == nullptr) {
// not readable
return res;
}
res.width = desc.width;
res.height = desc.height;
auto& new_frame = res.frames.emplace_back();
new_frame.ms = 0;
new_frame.data.insert(new_frame.data.cbegin(), img_data, img_data+(desc.width*desc.height*4));
free(img_data);
return res;
}
std::vector<uint8_t> ImageEncoderQOI::encodeToMemoryRGBA(const ImageResult& input_image, const std::map<std::string, float>&) {
if (input_image.frames.empty()) {
std::cerr << "IEQOI error: empty image\n";
return {};
}
if (input_image.frames.size() > 1) {
std::cerr << "IEQOI warning: image with animation, only first frame will be encoded!\n";
return {};
}
// TODO: look into RDO (eg https://github.com/richgel999/rdopng)
//int png_compression_level = 8;
//if (extra_options.count("png_compression_level")) {
//png_compression_level = extra_options.at("png_compression_level");
//}
qoi_desc desc;
desc.width = input_image.width;
desc.height = input_image.height;
desc.channels = 4;
desc.colorspace = QOI_SRGB; // TODO: decide
int out_len {0};
uint8_t* enc_data = static_cast<uint8_t*>(qoi_encode(
input_image.frames.front().data.data(),
&desc,
&out_len
));
if (enc_data == nullptr) {
std::cerr << "IEQOI error: qoi_encode failed!\n";
return {};
}
std::vector<uint8_t> new_data(enc_data, enc_data+out_len);
free(enc_data); // TODO: a streaming encoder would be better
return new_data;
}

13
src/image_loader_qoi.hpp Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include "./image_loader.hpp"
struct ImageLoaderQOI : public ImageLoaderI {
ImageInfo loadInfoFromMemory(const uint8_t* data, uint64_t data_size) override;
ImageResult loadFromMemoryRGBA(const uint8_t* data, uint64_t data_size) override;
};
struct ImageEncoderQOI : public ImageEncoderI {
std::vector<uint8_t> encodeToMemoryRGBA(const ImageResult& input_image, const std::map<std::string, float>& extra_options = {}) override;
};

View File

@ -2,6 +2,7 @@
#include "./image_loader_webp.hpp" #include "./image_loader_webp.hpp"
#include "./image_loader_sdl_bmp.hpp" #include "./image_loader_sdl_bmp.hpp"
#include "./image_loader_qoi.hpp"
#include "./image_loader_stb.hpp" #include "./image_loader_stb.hpp"
#include <solanaceae/message3/components.hpp> #include <solanaceae/message3/components.hpp>
@ -77,6 +78,7 @@ MediaMetaInfoLoader::MediaMetaInfoLoader(RegistryMessageModel& rmm) : _rmm(rmm)
// HACK: make them be added externally? // HACK: make them be added externally?
_image_loaders.push_back(std::make_unique<ImageLoaderWebP>()); _image_loaders.push_back(std::make_unique<ImageLoaderWebP>());
_image_loaders.push_back(std::make_unique<ImageLoaderSDLBMP>()); _image_loaders.push_back(std::make_unique<ImageLoaderSDLBMP>());
_image_loaders.push_back(std::make_unique<ImageLoaderQOI>());
_image_loaders.push_back(std::make_unique<ImageLoaderSTB>()); _image_loaders.push_back(std::make_unique<ImageLoaderSTB>());
_rmm.subscribe(this, RegistryMessageModel_Event::message_construct); _rmm.subscribe(this, RegistryMessageModel_Event::message_construct);

View File

@ -1,6 +1,7 @@
#include "./message_image_loader.hpp" #include "./message_image_loader.hpp"
#include "./image_loader_sdl_bmp.hpp" #include "./image_loader_sdl_bmp.hpp"
#include "./image_loader_qoi.hpp"
#include "./image_loader_stb.hpp" #include "./image_loader_stb.hpp"
#include "./image_loader_webp.hpp" #include "./image_loader_webp.hpp"
#include "./media_meta_info_loader.hpp" #include "./media_meta_info_loader.hpp"
@ -19,6 +20,7 @@ uint64_t getTimeMS(void);
MessageImageLoader::MessageImageLoader(void) { MessageImageLoader::MessageImageLoader(void) {
_image_loaders.push_back(std::make_unique<ImageLoaderSDLBMP>()); _image_loaders.push_back(std::make_unique<ImageLoaderSDLBMP>());
_image_loaders.push_back(std::make_unique<ImageLoaderQOI>());
_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>());
} }

View File

@ -3,6 +3,7 @@
#include "./image_loader_sdl_bmp.hpp" #include "./image_loader_sdl_bmp.hpp"
#include "./image_loader_stb.hpp" #include "./image_loader_stb.hpp"
#include "./image_loader_webp.hpp" #include "./image_loader_webp.hpp"
#include "./image_loader_qoi.hpp"
#include <imgui/imgui.h> #include <imgui/imgui.h>
@ -13,6 +14,7 @@ uint64_t getTimeMS(void);
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>());
_image_loaders.push_back(std::make_unique<ImageLoaderQOI>());
_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>());
} }
@ -421,7 +423,7 @@ void SendImagePopup::render(float time_delta) {
if (compress) { if (compress) {
ImGui::SameLine(); ImGui::SameLine();
ImGui::Combo("##compression_type", &current_compressor, "webp\0jpeg\0png\n"); ImGui::Combo("##compression_type", &current_compressor, "webp\0jpeg\0png\0qoi\0");
ImGui::Indent(); ImGui::Indent();
// combo "webp""webp-lossless""png""jpg?" // combo "webp""webp-lossless""png""jpg?"
@ -486,6 +488,11 @@ void SendImagePopup::render(float time_delta) {
if (!new_data.empty()) { if (!new_data.empty()) {
_on_send(new_data, ".png"); _on_send(new_data, ".png");
} }
} else if (current_compressor == 3) {
new_data = ImageEncoderQOI{}.encodeToMemoryRGBA(tmp_img, {});;
if (!new_data.empty()) {
_on_send(new_data, ".qoi");
}
} }
// error // error

View File

@ -1,6 +1,7 @@
#include "./tox_avatar_loader.hpp" #include "./tox_avatar_loader.hpp"
#include "./image_loader_sdl_bmp.hpp" #include "./image_loader_sdl_bmp.hpp"
#include "./image_loader_qoi.hpp"
#include "./image_loader_stb.hpp" #include "./image_loader_stb.hpp"
#include "./image_loader_webp.hpp" #include "./image_loader_webp.hpp"
@ -21,6 +22,7 @@ uint64_t getTimeMS(void);
ToxAvatarLoader::ToxAvatarLoader(Contact3Registry& cr) : _cr(cr) { ToxAvatarLoader::ToxAvatarLoader(Contact3Registry& cr) : _cr(cr) {
_image_loaders.push_back(std::make_unique<ImageLoaderSDLBMP>()); _image_loaders.push_back(std::make_unique<ImageLoaderSDLBMP>());
_image_loaders.push_back(std::make_unique<ImageLoaderQOI>());
_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>());
} }