From 3d5503612be4897abea39f3b4a3ecad514ea7e81 Mon Sep 17 00:00:00 2001 From: Stenzek Date: Fri, 11 Oct 2024 22:11:05 +1000 Subject: [PATCH] Image: Span and Error-ify --- src/util/image.cpp | 261 +++++++++++++++++--------- src/util/image.h | 18 +- src/util/imgui_fullscreen.cpp | 11 +- src/util/postprocessing_shader_fx.cpp | 4 +- 4 files changed, 189 insertions(+), 105 deletions(-) diff --git a/src/util/image.cpp b/src/util/image.cpp index 4a7201ea7..b70db3075 100644 --- a/src/util/image.cpp +++ b/src/util/image.cpp @@ -5,8 +5,10 @@ #include "common/assert.h" #include "common/bitutils.h" +#include "common/error.h" #include "common/fastjmp.h" #include "common/file_system.h" +#include "common/heap_array.h" #include "common/log.h" #include "common/path.h" #include "common/scoped_guard.h" @@ -25,28 +27,28 @@ LOG_CHANNEL(Image); -static bool PNGBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size); -static bool PNGBufferSaver(const RGBA8Image& image, std::vector* buffer, u8 quality); -static bool PNGFileLoader(RGBA8Image* image, std::string_view filename, std::FILE* fp); -static bool PNGFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE* fp, u8 quality); +static bool PNGBufferLoader(RGBA8Image* image, std::span data, Error* error); +static bool PNGBufferSaver(const RGBA8Image& image, DynamicHeapArray* data, u8 quality, Error* error); +static bool PNGFileLoader(RGBA8Image* image, std::string_view filename, std::FILE* fp, Error* error); +static bool PNGFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE* fp, u8 quality, Error* error); -static bool JPEGBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size); -static bool JPEGBufferSaver(const RGBA8Image& image, std::vector* buffer, u8 quality); -static bool JPEGFileLoader(RGBA8Image* image, std::string_view filename, std::FILE* fp); -static bool JPEGFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE* fp, u8 quality); +static bool JPEGBufferLoader(RGBA8Image* image, std::span data, Error* error); +static bool JPEGBufferSaver(const RGBA8Image& image, DynamicHeapArray* data, u8 quality, Error* error); +static bool JPEGFileLoader(RGBA8Image* image, std::string_view filename, std::FILE* fp, Error* error); +static bool JPEGFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE* fp, u8 quality, Error* error); -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, std::string_view filename, std::FILE* fp); -static bool WebPFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE* fp, u8 quality); +static bool WebPBufferLoader(RGBA8Image* image, std::span data, Error* error); +static bool WebPBufferSaver(const RGBA8Image& image, DynamicHeapArray* data, u8 quality, Error* error); +static bool WebPFileLoader(RGBA8Image* image, std::string_view filename, std::FILE* fp, Error* error); +static bool WebPFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE* fp, u8 quality, Error* error); struct FormatHandler { const char* extension; - bool (*buffer_loader)(RGBA8Image*, const void*, size_t); - bool (*buffer_saver)(const RGBA8Image&, std::vector*, u8); - bool (*file_loader)(RGBA8Image*, std::string_view, std::FILE*); - bool (*file_saver)(const RGBA8Image&, std::string_view, std::FILE*, u8); + bool (*buffer_loader)(RGBA8Image*, std::span, Error*); + bool (*buffer_saver)(const RGBA8Image&, DynamicHeapArray*, u8, Error*); + bool (*file_loader)(RGBA8Image*, std::string_view, std::FILE*, Error*); + bool (*file_saver)(const RGBA8Image&, std::string_view, std::FILE*, u8, Error*); }; static constexpr FormatHandler s_format_handlers[] = { @@ -101,22 +103,23 @@ RGBA8Image& RGBA8Image::operator=(RGBA8Image&& move) return *this; } -bool RGBA8Image::LoadFromFile(const char* filename) +bool RGBA8Image::LoadFromFile(const char* filename, Error* error /* = nullptr */) { - auto fp = FileSystem::OpenManagedCFile(filename, "rb"); + auto fp = FileSystem::OpenManagedCFile(filename, "rb", error); if (!fp) return false; - return LoadFromFile(filename, fp.get()); + return LoadFromFile(filename, fp.get(), error); } -bool RGBA8Image::SaveToFile(const char* filename, u8 quality) const +bool RGBA8Image::SaveToFile(const char* filename, u8 quality /* = DEFAULT_SAVE_QUALITY */, + Error* error /* = nullptr */) const { - auto fp = FileSystem::OpenManagedCFile(filename, "wb"); + auto fp = FileSystem::OpenManagedCFile(filename, "wb", error); if (!fp) return false; - if (SaveToFile(filename, fp.get(), quality)) + if (SaveToFile(filename, fp.get(), quality, error)) return true; // save failed @@ -125,62 +128,71 @@ bool RGBA8Image::SaveToFile(const char* filename, u8 quality) const return false; } -bool RGBA8Image::LoadFromFile(std::string_view filename, std::FILE* fp) +bool RGBA8Image::LoadFromFile(std::string_view filename, std::FILE* fp, Error* error /* = nullptr */) { const std::string_view extension(Path::GetExtension(filename)); const FormatHandler* handler = GetFormatHandler(extension); if (!handler || !handler->file_loader) { - ERROR_LOG("Unknown extension '{}'", extension); + Error::SetStringFmt(error, "Unknown extension '{}'", extension); return false; } - return handler->file_loader(this, filename, fp); + return handler->file_loader(this, filename, fp, error); } -bool RGBA8Image::LoadFromBuffer(std::string_view filename, const void* buffer, size_t buffer_size) +bool RGBA8Image::LoadFromBuffer(std::string_view filename, std::span data, Error* error /* = nullptr */) { const std::string_view extension(Path::GetExtension(filename)); const FormatHandler* handler = GetFormatHandler(extension); if (!handler || !handler->buffer_loader) { - ERROR_LOG("Unknown extension '{}'", extension); + Error::SetStringFmt(error, "Unknown extension '{}'", extension); return false; } - return handler->buffer_loader(this, buffer, buffer_size); + return handler->buffer_loader(this, data, error); } -bool RGBA8Image::SaveToFile(std::string_view filename, std::FILE* fp, u8 quality) const +bool RGBA8Image::SaveToFile(std::string_view filename, std::FILE* fp, u8 quality /* = DEFAULT_SAVE_QUALITY */, + Error* error /* = nullptr */) const { const std::string_view extension(Path::GetExtension(filename)); const FormatHandler* handler = GetFormatHandler(extension); if (!handler || !handler->file_saver) { - ERROR_LOG("Unknown extension '{}'", extension); + Error::SetStringFmt(error, "Unknown extension '{}'", extension); return false; } - if (!handler->file_saver(*this, filename, fp, quality)) + if (!handler->file_saver(*this, filename, fp, quality, error)) return false; - return (std::fflush(fp) == 0); + if (std::fflush(fp) != 0) + { + Error::SetErrno(error, "fflush() failed: ", errno); + return false; + } + + return true; } -std::optional> RGBA8Image::SaveToBuffer(std::string_view filename, u8 quality) const +std::optional> RGBA8Image::SaveToBuffer(std::string_view filename, + u8 quality /* = DEFAULT_SAVE_QUALITY */, + Error* error /* = nullptr */) const { - std::optional> ret; + std::optional> ret; const std::string_view extension(Path::GetExtension(filename)); const FormatHandler* handler = GetFormatHandler(extension); if (!handler || !handler->file_saver) { - ERROR_LOG("Unknown extension '{}'", extension); + Error::SetStringFmt(error, "Unknown extension '{}'", extension); return ret; } - ret = std::vector(); - if (!handler->buffer_saver(*this, &ret.value(), quality)) + ret = DynamicHeapArray(); + if (!handler->buffer_saver(*this, &ret.value(), quality, error)) ret.reset(); return ret; @@ -226,6 +238,17 @@ void RGBA8Image::Resize(const RGBA8Image* src_image, u32 new_width, u32 new_heig #endif +static void PNGSetErrorFunction(png_structp png_ptr, Error* error) +{ + png_set_error_fn( + png_ptr, error, + [](png_structp png_ptr, png_const_charp message) { + Error::SetStringView(static_cast(png_get_error_ptr(png_ptr)), message); + png_longjmp(png_ptr, 1); + }, + [](png_structp png_ptr, png_const_charp message) { WARNING_LOG("libpng warning: {}", message); }); +} + static bool PNGCommonLoader(RGBA8Image* image, png_structp png_ptr, png_infop info_ptr, std::vector& new_data, std::vector& row_pointers) { @@ -271,15 +294,19 @@ static bool PNGCommonLoader(RGBA8Image* image, png_structp png_ptr, png_infop in return true; } -bool PNGFileLoader(RGBA8Image* image, std::string_view filename, std::FILE* fp) +bool PNGFileLoader(RGBA8Image* image, std::string_view filename, std::FILE* fp, Error* error) { png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (!png_ptr) + { + Error::SetStringView(error, "png_create_read_struct() failed."); return false; + } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { + Error::SetStringView(error, "png_create_info_struct() failed."); png_destroy_read_struct(&png_ptr, nullptr, nullptr); return false; } @@ -289,27 +316,32 @@ bool PNGFileLoader(RGBA8Image* image, std::string_view filename, std::FILE* fp) std::vector new_data; std::vector row_pointers; + PNGSetErrorFunction(png_ptr, error); if (setjmp(png_jmpbuf(png_ptr))) return false; png_set_read_fn(png_ptr, fp, [](png_structp png_ptr, png_bytep data_ptr, png_size_t size) { std::FILE* fp = static_cast(png_get_io_ptr(png_ptr)); if (std::fread(data_ptr, size, 1, fp) != 1) - png_error(png_ptr, "Read error"); + png_error(png_ptr, "fread() failed"); }); return PNGCommonLoader(image, png_ptr, info_ptr, new_data, row_pointers); } -bool PNGBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size) +bool PNGBufferLoader(RGBA8Image* image, std::span data, Error* error) { png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); if (!png_ptr) + { + Error::SetStringView(error, "png_create_read_struct() failed."); return false; + } png_infop info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) { + Error::SetStringView(error, "png_create_info_struct() failed."); png_destroy_read_struct(&png_ptr, nullptr, nullptr); return false; } @@ -319,23 +351,23 @@ bool PNGBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size) std::vector new_data; std::vector row_pointers; + PNGSetErrorFunction(png_ptr, error); if (setjmp(png_jmpbuf(png_ptr))) return false; struct IOData { - const u8* buffer; - size_t buffer_size; + std::span buffer; size_t buffer_pos; }; - IOData data = {static_cast(buffer), buffer_size, 0}; + IOData iodata = {data, 0}; - png_set_read_fn(png_ptr, &data, [](png_structp png_ptr, png_bytep data_ptr, png_size_t size) { + png_set_read_fn(png_ptr, &iodata, [](png_structp png_ptr, png_bytep data_ptr, png_size_t size) { IOData* data = static_cast(png_get_io_ptr(png_ptr)); - const size_t read_size = std::min(data->buffer_size - data->buffer_pos, size); + const size_t read_size = std::min(data->buffer.size() - data->buffer_pos, size); if (read_size > 0) { - std::memcpy(data_ptr, data->buffer + data->buffer_pos, read_size); + std::memcpy(data_ptr, &data->buffer[data->buffer_pos], read_size); data->buffer_pos += read_size; } }); @@ -356,12 +388,15 @@ static void PNGSaveCommon(const RGBA8Image& image, png_structp png_ptr, png_info png_write_end(png_ptr, nullptr); } -bool PNGFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE* fp, u8 quality) +bool PNGFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE* fp, u8 quality, Error* error) { png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); png_infop info_ptr = nullptr; if (!png_ptr) + { + Error::SetStringView(error, "png_create_write_struct() failed."); return false; + } ScopedGuard cleanup([&png_ptr, &info_ptr]() { if (png_ptr) @@ -370,8 +405,12 @@ bool PNGFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE* info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) + { + Error::SetStringView(error, "png_create_info_struct() failed."); return false; + } + PNGSetErrorFunction(png_ptr, error); if (setjmp(png_jmpbuf(png_ptr))) return false; @@ -379,7 +418,7 @@ bool PNGFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE* png_ptr, fp, [](png_structp png_ptr, png_bytep data_ptr, png_size_t size) { if (std::fwrite(data_ptr, size, 1, static_cast(png_get_io_ptr(png_ptr))) != 1) - png_error(png_ptr, "file write error"); + png_error(png_ptr, "fwrite() failed"); }, [](png_structp png_ptr) {}); @@ -387,12 +426,15 @@ bool PNGFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE* return true; } -bool PNGBufferSaver(const RGBA8Image& image, std::vector* buffer, u8 quality) +bool PNGBufferSaver(const RGBA8Image& image, DynamicHeapArray* data, u8 quality, Error* error) { png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); png_infop info_ptr = nullptr; if (!png_ptr) + { + Error::SetStringView(error, "png_create_write_struct() failed."); return false; + } ScopedGuard cleanup([&png_ptr, &info_ptr]() { if (png_ptr) @@ -401,24 +443,38 @@ bool PNGBufferSaver(const RGBA8Image& image, std::vector* buffer, u8 quality info_ptr = png_create_info_struct(png_ptr); if (!info_ptr) + { + Error::SetStringView(error, "png_create_info_struct() failed."); return false; + } - buffer->reserve(image.GetWidth() * image.GetHeight() * 2); + struct IOData + { + DynamicHeapArray* buffer; + size_t buffer_pos; + }; + IOData iodata = {data, 0}; + data->resize(image.GetWidth() * image.GetHeight() * 2); + + PNGSetErrorFunction(png_ptr, error); if (setjmp(png_jmpbuf(png_ptr))) return false; png_set_write_fn( - png_ptr, buffer, + png_ptr, data, [](png_structp png_ptr, png_bytep data_ptr, png_size_t size) { - std::vector* buffer = static_cast*>(png_get_io_ptr(png_ptr)); - const size_t old_pos = buffer->size(); - buffer->resize(old_pos + size); - std::memcpy(buffer->data() + old_pos, data_ptr, size); + IOData* iodata = static_cast(png_get_io_ptr(png_ptr)); + const size_t new_pos = iodata->buffer_pos + size; + if (new_pos > iodata->buffer->size()) + iodata->buffer->resize(std::max(new_pos, iodata->buffer->size() * 2)); + std::memcpy(iodata->buffer->data() + iodata->buffer_pos, data_ptr, size); + iodata->buffer_pos += size; }, [](png_structp png_ptr) {}); PNGSaveCommon(image, png_ptr, info_ptr, quality); + iodata.buffer->resize(iodata.buffer_pos); return true; } @@ -426,6 +482,7 @@ namespace { struct JPEGErrorHandler { jpeg_error_mgr err; + Error* errptr; fastjmp_buf jbuf; JPEGErrorHandler() @@ -439,14 +496,14 @@ struct JPEGErrorHandler JPEGErrorHandler* eh = (JPEGErrorHandler*)cinfo->err; char msg[JMSG_LENGTH_MAX]; eh->err.format_message(cinfo, msg); - ERROR_LOG("libjpeg fatal error: {}", msg); + Error::SetStringFmt(eh->errptr, "libjpeg fatal error: {}", msg); fastjmp_jmp(&eh->jbuf, 1); } }; } // namespace template -static bool WrapJPEGDecompress(RGBA8Image* image, T setup_func) +static bool WrapJPEGDecompress(RGBA8Image* image, Error* error, T setup_func) { std::vector scanline; jpeg_decompress_struct info = {}; @@ -467,13 +524,14 @@ static bool WrapJPEGDecompress(RGBA8Image* image, T setup_func) const int herr = jpeg_read_header(&info, TRUE); if (herr != JPEG_HEADER_OK) { - ERROR_LOG("jpeg_read_header() returned {}", herr); + Error::SetStringFmt(error, "jpeg_read_header() returned {}", herr); return false; } if (info.image_width == 0 || info.image_height == 0 || info.num_components < 3) { - ERROR_LOG("Invalid image dimensions: {}x{}x{}", info.image_width, info.image_height, info.num_components); + Error::SetStringFmt(error, "Invalid image dimensions: {}x{}x{}", info.image_width, info.image_height, + info.num_components); return false; } @@ -482,7 +540,7 @@ static bool WrapJPEGDecompress(RGBA8Image* image, T setup_func) if (!jpeg_start_decompress(&info)) { - ERROR_LOG("jpeg_start_decompress() returned failure"); + Error::SetStringFmt(error, "jpeg_start_decompress() returned failure"); return false; } @@ -495,7 +553,7 @@ static bool WrapJPEGDecompress(RGBA8Image* image, T setup_func) { if (jpeg_read_scanlines(&info, scanline_buffer, 1) != 1) { - ERROR_LOG("jpeg_read_scanlines() failed at row {}", y); + Error::SetStringFmt(error, "jpeg_read_scanlines() failed at row {}", y); result = false; break; } @@ -516,14 +574,14 @@ static bool WrapJPEGDecompress(RGBA8Image* image, T setup_func) return result; } -bool JPEGBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size) +bool JPEGBufferLoader(RGBA8Image* image, std::span data, Error* error) { - return WrapJPEGDecompress(image, [buffer, buffer_size](jpeg_decompress_struct& info) { - jpeg_mem_src(&info, static_cast(buffer), static_cast(buffer_size)); + return WrapJPEGDecompress(image, error, [data](jpeg_decompress_struct& info) { + jpeg_mem_src(&info, static_cast(data.data()), static_cast(data.size())); }); } -bool JPEGFileLoader(RGBA8Image* image, std::string_view filename, std::FILE* fp) +bool JPEGFileLoader(RGBA8Image* image, std::string_view filename, std::FILE* fp, Error* error) { static constexpr u32 BUFFER_SIZE = 16384; @@ -533,6 +591,7 @@ bool JPEGFileLoader(RGBA8Image* image, std::string_view filename, std::FILE* fp) std::FILE* fp; std::unique_ptr buffer; + Error* errptr; bool end_of_file; }; @@ -565,7 +624,7 @@ bool JPEGFileLoader(RGBA8Image* image, std::string_view filename, std::FILE* fp) const size_t seek_cur = static_cast(num_bytes) - skip_in_buffer; if (seek_cur > 0) { - if (FileSystem::FSeek64(cb->fp, static_cast(seek_cur), SEEK_CUR) != 0) + if (!FileSystem::FSeek64(cb->fp, static_cast(seek_cur), SEEK_CUR, cb->errptr)) { cb->end_of_file = true; return; @@ -577,14 +636,15 @@ bool JPEGFileLoader(RGBA8Image* image, std::string_view filename, std::FILE* fp) }, .fp = fp, .buffer = std::make_unique(BUFFER_SIZE), + .errptr = error, .end_of_file = false, }; - return WrapJPEGDecompress(image, [&cb](jpeg_decompress_struct& info) { info.src = &cb.mgr; }); + return WrapJPEGDecompress(image, error, [&cb](jpeg_decompress_struct& info) { info.src = &cb.mgr; }); } template -static bool WrapJPEGCompress(const RGBA8Image& image, u8 quality, T setup_func) +static bool WrapJPEGCompress(const RGBA8Image& image, u8 quality, Error* error, T setup_func) { std::vector scanline; jpeg_compress_struct info = {}; @@ -629,7 +689,7 @@ static bool WrapJPEGCompress(const RGBA8Image& image, u8 quality, T setup_func) if (jpeg_write_scanlines(&info, scanline_buffer, 1) != 1) { - ERROR_LOG("jpeg_write_scanlines() failed at row {}", y); + Error::SetStringFmt(error, "jpeg_write_scanlines() failed at row {}", y); result = false; break; } @@ -640,7 +700,7 @@ static bool WrapJPEGCompress(const RGBA8Image& image, u8 quality, T setup_func) return result; } -bool JPEGBufferSaver(const RGBA8Image& image, std::vector* buffer, u8 quality) +bool JPEGBufferSaver(const RGBA8Image& image, DynamicHeapArray* buffer, u8 quality, Error* error) { // give enough space to avoid reallocs buffer->resize(image.GetWidth() * image.GetHeight() * 2); @@ -648,7 +708,7 @@ bool JPEGBufferSaver(const RGBA8Image& image, std::vector* buffer, u8 qualit struct MemCallback { jpeg_destination_mgr mgr; - std::vector* buffer; + DynamicHeapArray* buffer; size_t buffer_used; }; @@ -675,10 +735,10 @@ bool JPEGBufferSaver(const RGBA8Image& image, std::vector* buffer, u8 qualit cb->buffer->resize(cb->buffer->size() - cb->mgr.free_in_buffer); }; - return WrapJPEGCompress(image, quality, [&cb](jpeg_compress_struct& info) { info.dest = &cb.mgr; }); + return WrapJPEGCompress(image, quality, error, [&cb](jpeg_compress_struct& info) { info.dest = &cb.mgr; }); } -bool JPEGFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE* fp, u8 quality) +bool JPEGFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE* fp, u8 quality, Error* error) { static constexpr u32 BUFFER_SIZE = 16384; @@ -688,6 +748,7 @@ bool JPEGFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE std::FILE* fp; std::unique_ptr buffer; + Error* errptr; bool write_error; }; @@ -702,7 +763,10 @@ bool JPEGFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE .empty_output_buffer = [](j_compress_ptr cinfo) -> boolean { FileCallback* cb = BASE_FROM_RECORD_FIELD(cinfo->dest, FileCallback, mgr); if (!cb->write_error) - cb->write_error |= (std::fwrite(cb->buffer.get(), 1, BUFFER_SIZE, cb->fp) != BUFFER_SIZE); + { + if ((cb->write_error = (std::fwrite(cb->buffer.get(), 1, BUFFER_SIZE, cb->fp) != BUFFER_SIZE))) + Error::SetErrno(cb->errptr, "fwrite() failed: ", errno); + } cb->mgr.next_output_byte = cb->buffer.get(); cb->mgr.free_in_buffer = BUFFER_SIZE; @@ -713,33 +777,37 @@ bool JPEGFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE FileCallback* cb = BASE_FROM_RECORD_FIELD(cinfo->dest, FileCallback, mgr); const size_t left = BUFFER_SIZE - cb->mgr.free_in_buffer; if (left > 0 && !cb->write_error) - cb->write_error |= (std::fwrite(cb->buffer.get(), 1, left, cb->fp) != left); + { + if ((cb->write_error = (std::fwrite(cb->buffer.get(), 1, left, cb->fp) != left))) + Error::SetErrno(cb->errptr, "fwrite() failed: ", errno); + } }, }, .fp = fp, .buffer = std::make_unique(BUFFER_SIZE), + .errptr = error, .write_error = false, }; - return (WrapJPEGCompress(image, quality, [&cb](jpeg_compress_struct& info) { info.dest = &cb.mgr; }) && + return (WrapJPEGCompress(image, quality, error, [&cb](jpeg_compress_struct& info) { info.dest = &cb.mgr; }) && !cb.write_error); } -bool WebPBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size) +bool WebPBufferLoader(RGBA8Image* image, std::span data, Error* error) { int width, height; - if (!WebPGetInfo(static_cast(buffer), buffer_size, &width, &height) || width <= 0 || height <= 0) + if (!WebPGetInfo(data.data(), data.size(), &width, &height) || width <= 0 || height <= 0) { - ERROR_LOG("WebPGetInfo() failed"); + Error::SetStringView(error, "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))) + if (!WebPDecodeRGBAInto(data.data(), data.size(), reinterpret_cast(pixels.data()), sizeof(u32) * pixels.size(), + sizeof(u32) * static_cast(width))) { - ERROR_LOG("WebPDecodeRGBAInto() failed"); + Error::SetStringView(error, "WebPDecodeRGBAInto() failed"); return false; } @@ -747,35 +815,44 @@ bool WebPBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size) return true; } -bool WebPBufferSaver(const RGBA8Image& image, std::vector* buffer, u8 quality) +bool WebPBufferSaver(const RGBA8Image& image, DynamicHeapArray* data, u8 quality, Error* error) { 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) + { + Error::SetStringFmt(error, "WebPEncodeRGBA() for {}x{} failed.", image.GetWidth(), image.GetHeight()); return false; + } - buffer->resize(encoded_size); - std::memcpy(buffer->data(), encoded_data, encoded_size); + data->resize(encoded_size); + std::memcpy(data->data(), encoded_data, encoded_size); WebPFree(encoded_data); return true; } -bool WebPFileLoader(RGBA8Image* image, std::string_view filename, std::FILE* fp) +bool WebPFileLoader(RGBA8Image* image, std::string_view filename, std::FILE* fp, Error* error) { - std::optional> data = FileSystem::ReadBinaryFile(fp); + std::optional> data = FileSystem::ReadBinaryFile(fp, error); if (!data.has_value()) return false; - return WebPBufferLoader(image, data->data(), data->size()); + return WebPBufferLoader(image, data->cspan(), error); } -bool WebPFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE* fp, u8 quality) +bool WebPFileSaver(const RGBA8Image& image, std::string_view filename, std::FILE* fp, u8 quality, Error* error) { - std::vector buffer; - if (!WebPBufferSaver(image, &buffer, quality)) + DynamicHeapArray buffer; + if (!WebPBufferSaver(image, &buffer, quality, error)) return false; - return (std::fwrite(buffer.data(), buffer.size(), 1, fp) == 1); + if (std::fwrite(buffer.data(), buffer.size(), 1, fp) != 1) + { + Error::SetErrno(error, "fwrite() failed: ", errno); + return false; + } + + return true; } \ No newline at end of file diff --git a/src/util/image.h b/src/util/image.h index 463e3327c..6bc915b38 100644 --- a/src/util/image.h +++ b/src/util/image.h @@ -3,15 +3,19 @@ #pragma once +#include "common/heap_array.h" #include "common/types.h" #include #include #include #include +#include #include #include +class Error; + template class Image { @@ -126,11 +130,13 @@ public: RGBA8Image& operator=(const RGBA8Image& copy); RGBA8Image& operator=(RGBA8Image&& move); - bool LoadFromFile(const char* filename); - bool LoadFromFile(std::string_view filename, std::FILE* fp); - bool LoadFromBuffer(std::string_view filename, const void* buffer, size_t buffer_size); + bool LoadFromFile(const char* filename, Error* error = nullptr); + bool LoadFromFile(std::string_view filename, std::FILE* fp, Error* error = nullptr); + bool LoadFromBuffer(std::string_view filename, std::span data, Error* error = nullptr); - bool SaveToFile(const char* filename, u8 quality = DEFAULT_SAVE_QUALITY) const; - bool SaveToFile(std::string_view filename, std::FILE* fp, u8 quality = DEFAULT_SAVE_QUALITY) const; - std::optional> SaveToBuffer(std::string_view filename, u8 quality = DEFAULT_SAVE_QUALITY) const; + bool SaveToFile(const char* filename, u8 quality = DEFAULT_SAVE_QUALITY, Error* error = nullptr) const; + bool SaveToFile(std::string_view filename, std::FILE* fp, u8 quality = DEFAULT_SAVE_QUALITY, + Error* error = nullptr) const; + std::optional> SaveToBuffer(std::string_view filename, u8 quality = DEFAULT_SAVE_QUALITY, + Error* error = nullptr) const; }; diff --git a/src/util/imgui_fullscreen.cpp b/src/util/imgui_fullscreen.cpp index 7f0840a1e..f8c54bbfa 100644 --- a/src/util/imgui_fullscreen.cpp +++ b/src/util/imgui_fullscreen.cpp @@ -301,17 +301,18 @@ std::unique_ptr ImGuiFullscreen::CreateTextureFromImage(const RGBA8I std::optional ImGuiFullscreen::LoadTextureImage(std::string_view path) { std::optional image; + Error error; + if (Path::IsAbsolute(path)) { - Error error; std::string path_str(path); auto fp = FileSystem::OpenManagedCFile(path_str.c_str(), "rb", &error); if (fp) { image = RGBA8Image(); - if (!image->LoadFromFile(path_str.c_str(), fp.get())) + if (!image->LoadFromFile(path_str.c_str(), fp.get(), &error)) { - ERROR_LOG("Failed to read texture file '{}'", path); + ERROR_LOG("Failed to read texture file '{}': {}", path, error.GetDescription()); image.reset(); } } @@ -326,9 +327,9 @@ std::optional ImGuiFullscreen::LoadTextureImage(std::string_view pat if (data.has_value()) { image = RGBA8Image(); - if (!image->LoadFromBuffer(path, data->data(), data->size())) + if (!image->LoadFromBuffer(path, data->cspan(), &error)) { - ERROR_LOG("Failed to read texture resource '{}'", path); + ERROR_LOG("Failed to read texture resource '{}': {}", path, error.GetDescription()); image.reset(); } } diff --git a/src/util/postprocessing_shader_fx.cpp b/src/util/postprocessing_shader_fx.cpp index 3b95f607c..faddafab8 100644 --- a/src/util/postprocessing_shader_fx.cpp +++ b/src/util/postprocessing_shader_fx.cpp @@ -1129,9 +1129,9 @@ bool PostProcessing::ReShadeFXShader::CreatePasses(GPUTexture::Format backbuffer // Might be a base file/resource instead. const std::string resource_name = Path::Combine("shaders/reshade/Textures", source); if (std::optional> resdata = Host::ReadResourceFile(resource_name.c_str(), true); - !resdata.has_value() || !image.LoadFromBuffer(resource_name.c_str(), resdata->data(), resdata->size())) + !resdata.has_value() || !image.LoadFromBuffer(resource_name.c_str(), resdata->cspan(), error)) { - Error::SetStringFmt(error, "Failed to load image '{}' (from '{}')", source, image_path); + Error::AddPrefixFmt(error, "Failed to load image '{}' (from '{}'): ", source, image_path); return false; } }