mirror of
https://github.com/RetroDECK/Duckstation.git
synced 2025-02-01 04:05:38 +00:00
Image: Don't use libjpeg stdio functions
Fixes I/O in debug builds.
This commit is contained in:
parent
cb6be52404
commit
59a13d91ea
|
@ -224,3 +224,6 @@ static constexpr u32 HOST_PAGE_SHIFT = 12;
|
|||
static_cast<std::underlying_type<type_>::type>(rhs)); \
|
||||
return lhs; \
|
||||
}
|
||||
|
||||
// Compute the address of a base type given a field offset.
|
||||
#define BASE_FROM_RECORD_FIELD(ptr, base_type, field) ((base_type*)(((char*)ptr) - offsetof(base_type, field)))
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
// SPDX-FileCopyrightText: 2019-2023 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-FileCopyrightText: 2019-2024 Connor McLaughlin <stenzek@gmail.com>
|
||||
// SPDX-License-Identifier: (GPL-3.0 OR CC-BY-NC-ND-4.0)
|
||||
|
||||
#define IMGUI_DEFINE_MATH_OPERATORS
|
||||
|
@ -10,6 +10,7 @@
|
|||
|
||||
#include "common/assert.h"
|
||||
#include "common/easing.h"
|
||||
#include "common/error.h"
|
||||
#include "common/file_system.h"
|
||||
#include "common/log.h"
|
||||
#include "common/lru_cache.h"
|
||||
|
@ -271,24 +272,37 @@ const std::shared_ptr<GPUTexture>& ImGuiFullscreen::GetPlaceholderTexture()
|
|||
std::optional<RGBA8Image> ImGuiFullscreen::LoadTextureImage(const char* path)
|
||||
{
|
||||
std::optional<RGBA8Image> image;
|
||||
|
||||
std::optional<std::vector<u8>> data;
|
||||
if (Path::IsAbsolute(path))
|
||||
data = FileSystem::ReadBinaryFile(path);
|
||||
else
|
||||
data = Host::ReadResourceFile(path, true);
|
||||
if (data.has_value())
|
||||
{
|
||||
image = RGBA8Image();
|
||||
if (!image->LoadFromBuffer(path, data->data(), data->size()))
|
||||
Error error;
|
||||
auto fp = FileSystem::OpenManagedCFile(path, "rb", &error);
|
||||
if (fp)
|
||||
{
|
||||
Log_ErrorPrintf("Failed to read texture resource '%s'", path);
|
||||
image.reset();
|
||||
image = RGBA8Image();
|
||||
if (!image->LoadFromFile(path, fp.get()))
|
||||
Log_ErrorFmt("Failed to read texture file '{}'", path);
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_ErrorFmt("Failed to open texture file '{}': {}", path, error.GetDescription());
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_ErrorPrintf("Failed to open texture resource '%s'", path);
|
||||
std::optional<std::vector<u8>> data = Host::ReadResourceFile(path, true);
|
||||
if (data.has_value())
|
||||
{
|
||||
image = RGBA8Image();
|
||||
if (!image->LoadFromBuffer(path, data->data(), data->size()))
|
||||
{
|
||||
Log_ErrorFmt("Failed to read texture resource '{}'", path);
|
||||
image.reset();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Log_ErrorFmt("Failed to open texture resource '{}'", path);
|
||||
}
|
||||
}
|
||||
|
||||
return image;
|
||||
|
|
Loading…
Reference in a new issue