From 1bc1e942089043418cc4415af313b86e4155bff6 Mon Sep 17 00:00:00 2001 From: Morris Tabor <80684659+mortarroad@users.noreply.github.com> Date: Mon, 12 Apr 2021 18:56:50 +0200 Subject: Implement lossless WebP encoding --- modules/webp/image_loader_webp.cpp | 75 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 71 insertions(+), 4 deletions(-) (limited to 'modules') diff --git a/modules/webp/image_loader_webp.cpp b/modules/webp/image_loader_webp.cpp index 1f2a456619..772445190c 100644 --- a/modules/webp/image_loader_webp.cpp +++ b/modules/webp/image_loader_webp.cpp @@ -30,6 +30,7 @@ #include "image_loader_webp.h" +#include "core/config/project_settings.h" #include "core/io/marshalls.h" #include "core/os/os.h" #include "core/string/print_string.h" @@ -69,12 +70,77 @@ static Vector _webp_lossy_pack(const Ref &p_image, float p_quali w[2] = 'B'; w[3] = 'P'; memcpy(&w[4], dst_buff, dst_size); - free(dst_buff); + WebPFree(dst_buff); return dst; } -static Ref _webp_lossy_unpack(const Vector &p_buffer) { +static Vector _webp_lossless_pack(const Ref &p_image) { + ERR_FAIL_COND_V(p_image.is_null() || p_image->is_empty(), Vector()); + + int compression_level = ProjectSettings::get_singleton()->get("rendering/textures/lossless_compression/webp_compression_level"); + compression_level = CLAMP(compression_level, 0, 9); + + Ref img = p_image->duplicate(); + if (img->detect_alpha()) { + img->convert(Image::FORMAT_RGBA8); + } else { + img->convert(Image::FORMAT_RGB8); + } + + Size2 s(img->get_width(), img->get_height()); + Vector data = img->get_data(); + const uint8_t *r = data.ptr(); + + // we need to use the more complex API in order to access the 'exact' flag... + + WebPConfig config; + WebPPicture pic; + if (!WebPConfigInit(&config) || !WebPConfigLosslessPreset(&config, compression_level) || !WebPPictureInit(&pic)) { + ERR_FAIL_V(Vector()); + } + + WebPMemoryWriter wrt; + config.exact = 1; + pic.use_argb = 1; + pic.width = s.width; + pic.height = s.height; + pic.writer = WebPMemoryWrite; + pic.custom_ptr = &wrt; + WebPMemoryWriterInit(&wrt); + + bool success_import = false; + if (img->get_format() == Image::FORMAT_RGB8) { + success_import = WebPPictureImportRGB(&pic, r, 3 * s.width); + } else { + success_import = WebPPictureImportRGBA(&pic, r, 4 * s.width); + } + bool success_encode = false; + if (success_import) { + success_encode = WebPEncode(&config, &pic); + } + WebPPictureFree(&pic); + + if (!success_encode) { + WebPMemoryWriterClear(&wrt); + ERR_FAIL_V_MSG(Vector(), "WebP packing failed."); + } + + // copy from wrt + Vector dst; + dst.resize(4 + wrt.size); + uint8_t *w = dst.ptrw(); + w[0] = 'W'; + w[1] = 'E'; + w[2] = 'B'; + w[3] = 'P'; + memcpy(&w[4], wrt.mem, wrt.size); + WebPMemoryWriterClear(&wrt); + + return dst; +} + +static Ref _webp_unpack(const Vector &p_buffer) { int size = p_buffer.size() - 4; ERR_FAIL_COND_V(size <= 0, Ref()); const uint8_t *r = p_buffer.ptr(); @@ -168,6 +234,7 @@ void ImageLoaderWEBP::get_recognized_extensions(List *p_extensions) cons ImageLoaderWEBP::ImageLoaderWEBP() { Image::_webp_mem_loader_func = _webp_mem_loader_func; - Image::lossy_packer = _webp_lossy_pack; - Image::lossy_unpacker = _webp_lossy_unpack; + Image::webp_lossy_packer = _webp_lossy_pack; + Image::webp_lossless_packer = _webp_lossless_pack; + Image::webp_unpacker = _webp_unpack; } -- cgit v1.2.3