Image: Don't use libjpeg stdio functions

Fixes I/O in debug builds.
This commit is contained in:
Stenzek
2024-04-03 22:53:35 +10:00
parent cb6be52404
commit 59a13d91ea
3 changed files with 128 additions and 14 deletions

View File

@ -518,7 +518,62 @@ bool JPEGBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size)
bool JPEGFileLoader(RGBA8Image* image, const char* filename, std::FILE* fp)
{
return WrapJPEGDecompress(image, [fp](jpeg_decompress_struct& info) { jpeg_stdio_src(&info, fp); });
static constexpr u32 BUFFER_SIZE = 16384;
struct FileCallback
{
jpeg_source_mgr mgr;
std::FILE* fp;
std::unique_ptr<u8[]> buffer;
bool end_of_file;
};
FileCallback cb = {
.mgr = {
.init_source = [](j_decompress_ptr cinfo) {},
.fill_input_buffer = [](j_decompress_ptr cinfo) -> boolean {
FileCallback* cb = BASE_FROM_RECORD_FIELD(cinfo->src, FileCallback, mgr);
cb->mgr.next_input_byte = cb->buffer.get();
if (cb->end_of_file)
{
cb->buffer[0] = 0xFF;
cb->buffer[1] = JPEG_EOI;
cb->mgr.bytes_in_buffer = 2;
return TRUE;
}
const size_t r = std::fread(cb->buffer.get(), 1, BUFFER_SIZE, cb->fp);
cb->end_of_file |= (std::feof(cb->fp) != 0);
cb->mgr.bytes_in_buffer = r;
return TRUE;
},
.skip_input_data =
[](j_decompress_ptr cinfo, long num_bytes) {
FileCallback* cb = BASE_FROM_RECORD_FIELD(cinfo->src, FileCallback, mgr);
const size_t skip_in_buffer = std::min<size_t>(cb->mgr.bytes_in_buffer, static_cast<size_t>(num_bytes));
cb->mgr.next_input_byte += skip_in_buffer;
cb->mgr.bytes_in_buffer -= skip_in_buffer;
const size_t seek_cur = static_cast<size_t>(num_bytes) - skip_in_buffer;
if (seek_cur > 0)
{
if (FileSystem::FSeek64(cb->fp, static_cast<size_t>(seek_cur), SEEK_CUR) != 0)
{
cb->end_of_file = true;
return;
}
}
},
.resync_to_restart = jpeg_resync_to_restart,
.term_source = [](j_decompress_ptr cinfo) {},
},
.fp = fp,
.buffer = std::make_unique<u8[]>(BUFFER_SIZE),
.end_of_file = false,
};
return WrapJPEGDecompress(image, [&cb](jpeg_decompress_struct& info) { info.src = &cb.mgr; });
}
template<typename T>
@ -613,7 +668,49 @@ bool JPEGBufferSaver(const RGBA8Image& image, std::vector<u8>* buffer, u8 qualit
bool JPEGFileSaver(const RGBA8Image& image, const char* filename, std::FILE* fp, u8 quality)
{
return WrapJPEGCompress(image, quality, [fp](jpeg_compress_struct& info) { jpeg_stdio_dest(&info, fp); });
static constexpr u32 BUFFER_SIZE = 16384;
struct FileCallback
{
jpeg_destination_mgr mgr;
std::FILE* fp;
std::unique_ptr<u8[]> buffer;
bool write_error;
};
FileCallback cb = {
.mgr = {
.init_destination =
[](j_compress_ptr cinfo) {
FileCallback* cb = BASE_FROM_RECORD_FIELD(cinfo->dest, FileCallback, mgr);
cb->mgr.next_output_byte = cb->buffer.get();
cb->mgr.free_in_buffer = BUFFER_SIZE;
},
.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);
cb->mgr.next_output_byte = cb->buffer.get();
cb->mgr.free_in_buffer = BUFFER_SIZE;
return TRUE;
},
.term_destination =
[](j_compress_ptr cinfo) {
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);
},
},
.fp = fp,
.buffer = std::make_unique<u8[]>(BUFFER_SIZE),
.write_error = false,
};
return (WrapJPEGCompress(image, quality, [&cb](jpeg_compress_struct& info) { info.dest = &cb.mgr; }) &&
!cb.write_error);
}
bool WebPBufferLoader(RGBA8Image* image, const void* buffer, size_t buffer_size)