diff --git a/src/util/image.cpp b/src/util/image.cpp index 7e2699254..e5af1007d 100644 --- a/src/util/image.cpp +++ b/src/util/image.cpp @@ -13,6 +13,8 @@ #include #include +#include +#include // clang-format off #ifdef _MSC_VER @@ -33,6 +35,11 @@ static bool JPEGBufferSaver(const RGBA8Image& image, std::vector* buffer, u8 static bool JPEGFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp); static bool JPEGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, u8 quality); +static bool WebPBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size); +static bool WebPBufferSaver(const RGBA8Image& image, std::vector* buffer, u8 quality); +static bool WebPFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp); +static bool WebPFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, u8 quality); + struct FormatHandler { const char* extension; @@ -46,6 +53,7 @@ static constexpr FormatHandler s_format_handlers[] = { {"png", PNGBufferLoader, PNGBufferSaver, PNGFileLoader, PNGFileSaver}, {"jpg", JPEGBufferLoader, JPEGBufferSaver, JPEGFileLoader, JPEGFileSaver}, {"jpeg", JPEGBufferLoader, JPEGBufferSaver, JPEGFileLoader, JPEGFileSaver}, + {"webp", WebPBufferLoader, WebPBufferSaver, WebPFileLoader, WebPFileSaver}, }; static const FormatHandler* GetFormatHandler(const std::string_view& extension) @@ -607,3 +615,58 @@ bool JPEGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, { return WrapJPEGCompress(image, quality, [fp](jpeg_compress_struct& info) { jpeg_stdio_dest(&info, fp); }); } + +bool WebPBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size) +{ + int width, height; + if (!WebPGetInfo(static_cast(buffer), buffer_size, &width, &height) || width <= 0 || height <= 0) + { + Log_ErrorPrint("WebPGetInfo() failed"); + return false; + } + + std::vector pixels; + pixels.resize(static_cast(width) * static_cast(height)); + if (!WebPDecodeRGBAInto(static_cast(buffer), buffer_size, reinterpret_cast(pixels.data()), + sizeof(u32) * pixels.size(), sizeof(u32) * static_cast(width))) + { + Log_ErrorPrint("WebPDecodeRGBAInto() failed"); + return false; + } + + image->SetPixels(static_cast(width), static_cast(height), std::move(pixels)); + return true; +} + +bool WebPBufferSaver(const RGBA8Image& image, std::vector* buffer, u8 quality) +{ + u8* encoded_data; + const size_t encoded_size = + WebPEncodeRGBA(reinterpret_cast(image.GetPixels()), image.GetWidth(), image.GetHeight(), + image.GetPitch(), static_cast(quality), &encoded_data); + if (encoded_size == 0) + return false; + + buffer->resize(encoded_size); + std::memcpy(buffer->data(), encoded_data, encoded_size); + WebPFree(encoded_data); + return true; +} + +bool WebPFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp) +{ + std::optional> data = FileSystem::ReadBinaryFile(fp); + if (!data.has_value()) + return false; + + return WebPBufferLoader(image, data->data(), data->size()); +} + +bool WebPFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, u8 quality) +{ + std::vector buffer; + if (!WebPBufferSaver(image, &buffer, quality)) + return false; + + return (std::fwrite(buffer.data(), buffer.size(), 1, fp) == 1); +} \ No newline at end of file