diff options
author | Vas Crabb <vas@vastheman.com> | 2021-06-29 02:54:16 +1000 |
---|---|---|
committer | Vas Crabb <vas@vastheman.com> | 2021-06-29 05:40:17 +1000 |
commit | 18737febb5c76d38a464dfdb760f6f2a6a32af14 (patch) | |
tree | 1487423bc4bee2e4ea73cab12578fdcbe9114865 | |
parent | 8c12c611817cbade57ca6ab5235436a6e766ea34 (diff) |
emu/rendutil.cpp: Use setjmp/longjmp for libjpeg error unwinding.
Apparently Linux AArch64 has issues with throwing exceptions across C
function frames.
-rw-r--r-- | src/emu/rendutil.cpp | 160 |
1 files changed, 90 insertions, 70 deletions
diff --git a/src/emu/rendutil.cpp b/src/emu/rendutil.cpp index 443887b6305..a902a51bd85 100644 --- a/src/emu/rendutil.cpp +++ b/src/emu/rendutil.cpp @@ -17,6 +17,9 @@ #include "jpeglib.h" #include "jerror.h" +#include <csetjmp> +#include <cstdlib> + namespace { @@ -113,6 +116,18 @@ void jpeg_corefile_source::source(j_decompress_ptr cinfo, util::core_file &file) src->next_input_byte = nullptr; } + +struct jpeg_setjmp_error_mgr : public jpeg_error_mgr +{ + jpeg_setjmp_error_mgr() + { + jpeg_std_error(this); + error_exit = [] (j_common_ptr cinfo) { std::longjmp(static_cast<jpeg_setjmp_error_mgr *>(cinfo->err)->m_jump_buffer, 1); }; + } + + std::jmp_buf m_jump_buffer; +}; + } // anonymous namespace @@ -672,73 +687,74 @@ void render_load_jpeg(bitmap_argb32 &bitmap, util::core_file &file) // deallocate previous bitmap bitmap.reset(); - // create a JPEG source for the file + // set up context for error handling jpeg_decompress_struct cinfo; - jpeg_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr); - jerr.error_exit = [] (j_common_ptr cinfo) { throw cinfo->err; }; + jpeg_setjmp_error_mgr jerr; + cinfo.err = &jerr; JSAMPARRAY buffer = nullptr; - try + int w, h, s, row_stride, j, i; + if (setjmp(jerr.m_jump_buffer)) // setjmp must be used in control expression { - jpeg_create_decompress(&cinfo); - cinfo.mem->max_memory_to_use = 128 * 1024 * 1024; - jpeg_corefile_source::source(&cinfo, file); - - // read JPEG header and start decompression - jpeg_read_header(&cinfo, TRUE); - jpeg_start_decompress(&cinfo); - - // allocates the destination bitmap - int w = cinfo.output_width; - int h = cinfo.output_height; - int s = cinfo.output_components; - bitmap.allocate(w, h); + char msg[1024]; + (cinfo.err->format_message)(reinterpret_cast<j_common_ptr>(&cinfo), msg); + osd_printf_error("JPEG error reading data from file: %s\n", msg); + bitmap.reset(); + goto cleanup; // use goto to ensure longjmp can't cross an initialisation + } - // allocates a buffer to receive the information and copy them into the bitmap - int row_stride = cinfo.output_width * cinfo.output_components; - JSAMPARRAY buffer = reinterpret_cast<JSAMPARRAY>(malloc(sizeof(JSAMPROW))); - buffer[0] = reinterpret_cast<JSAMPROW>(malloc(sizeof(JSAMPLE) * row_stride)); + // create a JPEG source for the file + jpeg_create_decompress(&cinfo); + cinfo.mem->max_memory_to_use = 128 * 1024 * 1024; + jpeg_corefile_source::source(&cinfo, file); + + // read JPEG header and start decompression + jpeg_read_header(&cinfo, TRUE); + jpeg_start_decompress(&cinfo); + + // allocates the destination bitmap + w = cinfo.output_width; + h = cinfo.output_height; + s = cinfo.output_components; + bitmap.allocate(w, h); + + // allocates a buffer to receive the information and copy them into the bitmap + row_stride = cinfo.output_width * cinfo.output_components; + buffer = reinterpret_cast<JSAMPARRAY>(std::malloc(sizeof(JSAMPROW))); + buffer[0] = reinterpret_cast<JSAMPROW>(std::malloc(sizeof(JSAMPLE) * row_stride)); + + while (cinfo.output_scanline < cinfo.output_height) + { + j = cinfo.output_scanline; + jpeg_read_scanlines(&cinfo, buffer, 1); - while (cinfo.output_scanline < cinfo.output_height) + if (s == 1) { - int j = cinfo.output_scanline; - jpeg_read_scanlines(&cinfo, buffer, 1); - - if (s == 1) - { - for (int i = 0; i < w; ++i) - bitmap.pix(j, i) = rgb_t(0xFF, buffer[0][i], buffer[0][i], buffer[0][i]); + for (i = 0; i < w; ++i) + bitmap.pix(j, i) = rgb_t(0xff, buffer[0][i], buffer[0][i], buffer[0][i]); - } - else if (s == 3) - { - for (int i = 0; i < w; ++i) - bitmap.pix(j, i) = rgb_t(0xFF, buffer[0][i * s], buffer[0][i * s + 1], buffer[0][i * s + 2]); - } - else - { - osd_printf_error("Cannot read JPEG data from file.\n"); - bitmap.reset(); - break; - } } - - // finish decompression and frees the memory - jpeg_finish_decompress(&cinfo); - } - catch (jpeg_error_mgr *) - { - char msg[1024]; - (cinfo.err->format_message)(reinterpret_cast<j_common_ptr>(&cinfo), msg); - osd_printf_error("JPEG error reading data from file: %s\n", msg); - bitmap.reset(); + else if (s == 3) + { + for (i = 0; i < w; ++i) + bitmap.pix(j, i) = rgb_t(0xff, buffer[0][i * s], buffer[0][i * s + 1], buffer[0][i * s + 2]); + } + else + { + osd_printf_error("Cannot read JPEG data from file.\n"); + bitmap.reset(); + break; + } } + + // finish decompression and free the memory + jpeg_finish_decompress(&cinfo); +cleanup: jpeg_destroy_decompress(&cinfo); if (buffer) { if (buffer[0]) - free(buffer[0]); - free(buffer); + std::free(buffer[0]); + std::free(buffer); } } @@ -917,18 +933,21 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const util::png_info ru_imgformat render_detect_image(util::core_file &file) { // PNG: check for valid header - util::png_error const png = util::png_info::verify_header(file); - file.seek(0, SEEK_SET); - if (util::png_error::NONE == png) - return RENDUTIL_IMGFORMAT_PNG; + { + util::png_error const png = util::png_info::verify_header(file); + file.seek(0, SEEK_SET); + if (util::png_error::NONE == png) + return RENDUTIL_IMGFORMAT_PNG; + } // JPEG: attempt to read header with libjpeg - jpeg_decompress_struct cinfo; - jpeg_error_mgr jerr; - cinfo.err = jpeg_std_error(&jerr); - jerr.error_exit = [] (j_common_ptr cinfo) { throw cinfo->err; }; - try { + jpeg_decompress_struct cinfo; + jpeg_setjmp_error_mgr jerr; + cinfo.err = &jerr; + if (setjmp(jerr.m_jump_buffer)) // setjmp must be used in control expression + goto notjpeg; // use goto to ensure longjmp can't cross an initialisation + jpeg_create_decompress(&cinfo); cinfo.mem->max_memory_to_use = 128 * 1024 * 1024; jpeg_corefile_source::source(&cinfo, file); @@ -936,18 +955,19 @@ ru_imgformat render_detect_image(util::core_file &file) jpeg_destroy_decompress(&cinfo); file.seek(0, SEEK_SET); return RENDUTIL_IMGFORMAT_JPEG; - } - catch (jpeg_error_mgr *) - { + + notjpeg: jpeg_destroy_decompress(&cinfo); file.seek(0, SEEK_SET); } // Microsoft DIB: check for valid header - util::msdib_error const msdib = util::msdib_verify_header(file); - file.seek(0, SEEK_SET); - if (util::msdib_error::NONE == msdib) - return RENDUTIL_IMGFORMAT_MSDIB; + { + util::msdib_error const msdib = util::msdib_verify_header(file); + file.seek(0, SEEK_SET); + if (util::msdib_error::NONE == msdib) + return RENDUTIL_IMGFORMAT_MSDIB; + } // TODO: add more as necessary return RENDUTIL_IMGFORMAT_UNKNOWN; |