extract webp image encoder
This commit is contained in:
parent
621327bf55
commit
f1f67fe1ba
@ -1,7 +1,9 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
|
#include <map>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
struct ImageLoaderI {
|
struct ImageLoaderI {
|
||||||
virtual ~ImageLoaderI(void) {}
|
virtual ~ImageLoaderI(void) {}
|
||||||
@ -26,3 +28,12 @@ struct ImageLoaderI {
|
|||||||
virtual ImageResult loadFromMemoryRGBA(const uint8_t* data, uint64_t data_size) = 0;
|
virtual ImageResult loadFromMemoryRGBA(const uint8_t* data, uint64_t data_size) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ImageEncoderI {
|
||||||
|
virtual ~ImageEncoderI(void) {}
|
||||||
|
|
||||||
|
using ImageResult = ImageLoaderI::ImageResult;
|
||||||
|
|
||||||
|
virtual std::vector<uint8_t> encodeToMemoryRGBA(const ImageResult& input_image) = 0;
|
||||||
|
virtual std::vector<uint8_t> encodeToMemoryRGBAExt(const ImageResult& input_image, const std::map<std::string, float>& extra_options) = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
@ -2,9 +2,12 @@
|
|||||||
|
|
||||||
#include <webp/demux.h>
|
#include <webp/demux.h>
|
||||||
#include <webp/mux.h>
|
#include <webp/mux.h>
|
||||||
|
#include <webp/encode.h>
|
||||||
|
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
ImageLoaderWebP::ImageInfo ImageLoaderWebP::loadInfoFromMemory(const uint8_t* data, uint64_t data_size) {
|
ImageLoaderWebP::ImageInfo ImageLoaderWebP::loadInfoFromMemory(const uint8_t* data, uint64_t data_size) {
|
||||||
ImageInfo res;
|
ImageInfo res;
|
||||||
|
|
||||||
@ -78,3 +81,88 @@ ImageLoaderWebP::ImageResult ImageLoaderWebP::loadFromMemoryRGBA(const uint8_t*
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> ImageEncoderWebP::encodeToMemoryRGBA(const ImageResult& input_image) {
|
||||||
|
return encodeToMemoryRGBAExt(input_image, {{"quality", 80.f}});
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> ImageEncoderWebP::encodeToMemoryRGBAExt(const ImageResult& input_image, const std::map<std::string, float>& 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<uint8_t> new_data{webp_data.bytes, webp_data.bytes+webp_data.size};
|
||||||
|
|
||||||
|
WebPDataClear(&webp_data);
|
||||||
|
|
||||||
|
return new_data;
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -7,3 +7,8 @@ struct ImageLoaderWebP : public ImageLoaderI {
|
|||||||
ImageResult loadFromMemoryRGBA(const uint8_t* data, uint64_t data_size) override;
|
ImageResult loadFromMemoryRGBA(const uint8_t* data, uint64_t data_size) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct ImageEncoderWebP : public ImageEncoderI {
|
||||||
|
std::vector<uint8_t> encodeToMemoryRGBA(const ImageResult& input_image) override;
|
||||||
|
std::vector<uint8_t> encodeToMemoryRGBAExt(const ImageResult& input_image, const std::map<std::string, float>& extra_options) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
@ -6,10 +6,6 @@
|
|||||||
|
|
||||||
#include <imgui/imgui.h>
|
#include <imgui/imgui.h>
|
||||||
|
|
||||||
// tmp
|
|
||||||
#include <webp/mux.h>
|
|
||||||
#include <webp/encode.h>
|
|
||||||
|
|
||||||
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<ImageLoaderWebP>());
|
_image_loaders.push_back(std::make_unique<ImageLoaderWebP>());
|
||||||
@ -105,78 +101,9 @@ bool SendImagePopup::load(void) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::vector<uint8_t> SendImagePopup::compressWebp(const ImageLoaderI::ImageResult& input_image, uint32_t quality) {
|
std::vector<uint8_t> SendImagePopup::compressWebp(const ImageLoaderI::ImageResult& input_image, uint32_t quality) {
|
||||||
// HACK: move to own interface
|
// HACK: generic list
|
||||||
|
|
||||||
WebPAnimEncoderOptions enc_options;
|
return ImageEncoderWebP{}.encodeToMemoryRGBAExt(input_image, {{"quality", quality}});
|
||||||
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<uint8_t> new_data{webp_data.bytes, webp_data.bytes+webp_data.size};
|
|
||||||
|
|
||||||
WebPDataClear(&webp_data);
|
|
||||||
|
|
||||||
return new_data;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ImageLoaderI::ImageResult SendImagePopup::crop(const ImageLoaderI::ImageResult& input_image, const Rect& crop_rect) {
|
ImageLoaderI::ImageResult SendImagePopup::crop(const ImageLoaderI::ImageResult& input_image, const Rect& crop_rect) {
|
||||||
|
Loading…
Reference in New Issue
Block a user