From f1f67fe1bad07ff6814c57bee4cdcc092143beed Mon Sep 17 00:00:00 2001 From: Green Sky Date: Fri, 6 Oct 2023 02:01:31 +0200 Subject: [PATCH] extract webp image encoder --- src/image_loader.hpp | 11 +++++ src/image_loader_webp.cpp | 88 +++++++++++++++++++++++++++++++++++++++ src/image_loader_webp.hpp | 5 +++ src/send_image_popup.cpp | 77 +--------------------------------- 4 files changed, 106 insertions(+), 75 deletions(-) diff --git a/src/image_loader.hpp b/src/image_loader.hpp index d67ca72a..013b6b4b 100644 --- a/src/image_loader.hpp +++ b/src/image_loader.hpp @@ -1,7 +1,9 @@ #pragma once #include +#include #include +#include struct ImageLoaderI { virtual ~ImageLoaderI(void) {} @@ -26,3 +28,12 @@ struct ImageLoaderI { virtual ImageResult loadFromMemoryRGBA(const uint8_t* data, uint64_t data_size) = 0; }; +struct ImageEncoderI { + virtual ~ImageEncoderI(void) {} + + using ImageResult = ImageLoaderI::ImageResult; + + virtual std::vector encodeToMemoryRGBA(const ImageResult& input_image) = 0; + virtual std::vector encodeToMemoryRGBAExt(const ImageResult& input_image, const std::map& extra_options) = 0; +}; + diff --git a/src/image_loader_webp.cpp b/src/image_loader_webp.cpp index 17bd4d80..15cd3c66 100644 --- a/src/image_loader_webp.cpp +++ b/src/image_loader_webp.cpp @@ -2,9 +2,12 @@ #include #include +#include #include +#include + ImageLoaderWebP::ImageInfo ImageLoaderWebP::loadInfoFromMemory(const uint8_t* data, uint64_t data_size) { ImageInfo res; @@ -78,3 +81,88 @@ ImageLoaderWebP::ImageResult ImageLoaderWebP::loadFromMemoryRGBA(const uint8_t* return res; } +std::vector ImageEncoderWebP::encodeToMemoryRGBA(const ImageResult& input_image) { + return encodeToMemoryRGBAExt(input_image, {{"quality", 80.f}}); +} + +std::vector ImageEncoderWebP::encodeToMemoryRGBAExt(const ImageResult& input_image, const std::map& extra_options) { + // setup options + float quality = 80.f; + if (extra_options.count("quality")) { + quality = extra_options.at("quality"); + } + + // start encoding + + WebPAnimEncoderOptions enc_options; + if (!WebPAnimEncoderOptionsInit(&enc_options)) { + std::cerr << "IEWebP error: WebPAnimEncoderOptionsInit()\n"; + return {}; + } + + // Tune 'enc_options' as needed. + enc_options.minimize_size = 1; // might be slow? optimize for size, no key-frame insertion + + WebPAnimEncoder* enc = WebPAnimEncoderNew(input_image.width, input_image.height, &enc_options); + if (enc == nullptr) { + std::cerr << "IEWebP error: WebPAnimEncoderNew()\n"; + return {}; + } + + int prev_timestamp = 0; + for (const auto& frame : input_image.frames) { + WebPConfig config; + //WebPConfigInit(&config); + if (!WebPConfigPreset(&config, WebPPreset::WEBP_PRESET_DEFAULT, quality)) { + std::cerr << "IEWebP error: WebPConfigPreset()\n"; + WebPAnimEncoderDelete(enc); + return {}; + } + //WebPConfigLosslessPreset(&config, 6); // 9 for max compression + + WebPPicture frame_webp; + if (!WebPPictureInit(&frame_webp)) { + std::cerr << "IEWebP error: WebPPictureInit()\n"; + WebPAnimEncoderDelete(enc); + return {}; + } + frame_webp.width = input_image.width; + frame_webp.height = input_image.height; + if (!WebPPictureImportRGBA(&frame_webp, frame.data.data(), 4*input_image.width)) { + std::cerr << "IEWebP error: WebPPictureImportRGBA()\n"; + WebPAnimEncoderDelete(enc); + return {}; + } + + if (!WebPAnimEncoderAdd(enc, &frame_webp, prev_timestamp, &config)) { + std::cerr << "IEWebP error: WebPAnimEncoderAdd()\n"; + WebPPictureFree(&frame_webp); + WebPAnimEncoderDelete(enc); + return {}; + } + prev_timestamp += frame.ms; + } + if (!WebPAnimEncoderAdd(enc, NULL, prev_timestamp, NULL)) { // tell anim encoder its the end + std::cerr << "IEWebP error: WebPAnimEncoderAdd(NULL)\n"; + WebPAnimEncoderDelete(enc); + return {}; + } + + WebPData webp_data; + WebPDataInit(&webp_data); + if (!WebPAnimEncoderAssemble(enc, &webp_data)) { + std::cerr << "IEWebP error: WebPAnimEncoderAdd(NULL)\n"; + WebPAnimEncoderDelete(enc); + return {}; + } + WebPAnimEncoderDelete(enc); + + // Write the 'webp_data' to a file, or re-mux it further. + // TODO: make it not a copy + std::vector new_data{webp_data.bytes, webp_data.bytes+webp_data.size}; + + WebPDataClear(&webp_data); + + return new_data; +} + diff --git a/src/image_loader_webp.hpp b/src/image_loader_webp.hpp index f7e79e0c..812e78ba 100644 --- a/src/image_loader_webp.hpp +++ b/src/image_loader_webp.hpp @@ -7,3 +7,8 @@ struct ImageLoaderWebP : public ImageLoaderI { ImageResult loadFromMemoryRGBA(const uint8_t* data, uint64_t data_size) override; }; +struct ImageEncoderWebP : public ImageEncoderI { + std::vector encodeToMemoryRGBA(const ImageResult& input_image) override; + std::vector encodeToMemoryRGBAExt(const ImageResult& input_image, const std::map& extra_options) override; +}; + diff --git a/src/send_image_popup.cpp b/src/send_image_popup.cpp index c159eb08..b41ed3de 100644 --- a/src/send_image_popup.cpp +++ b/src/send_image_popup.cpp @@ -6,10 +6,6 @@ #include -// tmp -#include -#include - SendImagePopup::SendImagePopup(TextureUploaderI& tu) : _tu(tu) { _image_loaders.push_back(std::make_unique()); _image_loaders.push_back(std::make_unique()); @@ -105,78 +101,9 @@ bool SendImagePopup::load(void) { } std::vector SendImagePopup::compressWebp(const ImageLoaderI::ImageResult& input_image, uint32_t quality) { - // HACK: move to own interface + // HACK: generic list - WebPAnimEncoderOptions enc_options; - if (!WebPAnimEncoderOptionsInit(&enc_options)) { - std::cerr << "SIP error: WebPAnimEncoderOptionsInit()\n"; - return {}; - } - - // Tune 'enc_options' as needed. - enc_options.minimize_size = 1; // might be slow? optimize for size, no key-frame insertion - - WebPAnimEncoder* enc = WebPAnimEncoderNew(input_image.width, input_image.height, &enc_options); - if (enc == nullptr) { - std::cerr << "SIP error: WebPAnimEncoderNew()\n"; - return {}; - } - - int prev_timestamp = 0; - for (const auto& frame : input_image.frames) { - WebPConfig config; - //WebPConfigInit(&config); - if (!WebPConfigPreset(&config, WebPPreset::WEBP_PRESET_DEFAULT, quality)) { - std::cerr << "SIP error: WebPConfigPreset()\n"; - WebPAnimEncoderDelete(enc); - return {}; - } - //WebPConfigLosslessPreset(&config, 6); // 9 for max compression - - WebPPicture frame_webp; - if (!WebPPictureInit(&frame_webp)) { - std::cerr << "SIP error: WebPPictureInit()\n"; - WebPAnimEncoderDelete(enc); - return {}; - } - frame_webp.width = input_image.width; - frame_webp.height = input_image.height; - if (!WebPPictureImportRGBA(&frame_webp, frame.data.data(), 4*input_image.width)) { - std::cerr << "SIP error: WebPPictureImportRGBA()\n"; - WebPAnimEncoderDelete(enc); - return {}; - } - - if (!WebPAnimEncoderAdd(enc, &frame_webp, prev_timestamp, &config)) { - std::cerr << "SIP error: WebPAnimEncoderAdd()\n"; - WebPPictureFree(&frame_webp); - WebPAnimEncoderDelete(enc); - return {}; - } - prev_timestamp += frame.ms; - } - if (!WebPAnimEncoderAdd(enc, NULL, prev_timestamp, NULL)) { // tell anim encoder its the end - std::cerr << "SIP error: WebPAnimEncoderAdd(NULL)\n"; - WebPAnimEncoderDelete(enc); - return {}; - } - - WebPData webp_data; - WebPDataInit(&webp_data); - if (!WebPAnimEncoderAssemble(enc, &webp_data)) { - std::cerr << "SIP error: WebPAnimEncoderAdd(NULL)\n"; - WebPAnimEncoderDelete(enc); - return {}; - } - WebPAnimEncoderDelete(enc); - - // Write the 'webp_data' to a file, or re-mux it further. - // TODO: make it not a copy - std::vector new_data{webp_data.bytes, webp_data.bytes+webp_data.size}; - - WebPDataClear(&webp_data); - - return new_data; + return ImageEncoderWebP{}.encodeToMemoryRGBAExt(input_image, {{"quality", quality}}); } ImageLoaderI::ImageResult SendImagePopup::crop(const ImageLoaderI::ImageResult& input_image, const Rect& crop_rect) {