diff options
author | 2020-10-08 02:04:31 +1100 | |
---|---|---|
committer | 2020-10-08 02:04:31 +1100 | |
commit | b90fd355150a0f56e368bb33619459b19ec92bee (patch) | |
tree | 41ac320a4199b0856a485c9d75458dbc98aa168e /src/emu/rendutil.cpp | |
parent | 4b0903bfdbe88d4e220fc2c2f140e7b503060909 (diff) |
Various improvements to image file handling:
Moved MS DIB parser out of ICO file reader and made it available for
artwork and layout images.
Added more efficient I/O and better error checking for JPEG file loading
(MAME will no longer exit immediately on a bad JPEG file).
Made caller responsible for opening files for loading images, to avoid
decompressing images used in ZIP/7z artwork multiple times.
Added support for JPEG and Windows DIB to picture_image_device.
Added support for SVG image files in external artwork.
Added support for using I/O port value for animation state and masking
animation state values.
Made bounds elements more flexible in layouts.
Reworked headers to reduce dependencies.
Updated layout file format documentation.
Diffstat (limited to 'src/emu/rendutil.cpp')
-rw-r--r-- | src/emu/rendutil.cpp | 319 |
1 files changed, 221 insertions, 98 deletions
diff --git a/src/emu/rendutil.cpp b/src/emu/rendutil.cpp index 18755ffb340..c66492a8895 100644 --- a/src/emu/rendutil.cpp +++ b/src/emu/rendutil.cpp @@ -11,9 +11,109 @@ #include "emu.h" #include "rendutil.h" +#include "msdib.h" #include "png.h" #include "jpeglib.h" +#include "jerror.h" + + +namespace { + +struct jpeg_corefile_source : public jpeg_source_mgr +{ + static void source(j_decompress_ptr cinfo, util::core_file &file); + +private: + static constexpr unsigned INPUT_BUF_SIZE = 4096; + + static void do_init(j_decompress_ptr cinfo) + { + jpeg_corefile_source &src = *static_cast<jpeg_corefile_source *>(cinfo->src); + src.start_of_file = true; + } + + static boolean do_fill(j_decompress_ptr cinfo) + { + jpeg_corefile_source &src = *static_cast<jpeg_corefile_source *>(cinfo->src); + + size_t nbytes = src.infile->read(src.buffer, INPUT_BUF_SIZE); + + if (0 >= nbytes) + { + if (src.start_of_file) + ERREXIT(cinfo, JERR_INPUT_EMPTY); + WARNMS(cinfo, JWRN_JPEG_EOF); + src.buffer[0] = JOCTET(0xff); + src.buffer[1] = JOCTET(JPEG_EOI); + nbytes = 2; + } + + src.next_input_byte = src.buffer; + src.bytes_in_buffer = nbytes; + src.start_of_file = false; + + return TRUE; + } + + static void do_skip(j_decompress_ptr cinfo, long num_bytes) + { + jpeg_corefile_source &src = *static_cast<jpeg_corefile_source *>(cinfo->src); + + if (0 < num_bytes) + { + while (long(src.bytes_in_buffer) < num_bytes) + { + num_bytes -= long(src.bytes_in_buffer); + (void)(*src.fill_input_buffer)(cinfo); + } + src.next_input_byte += size_t(num_bytes); + src.bytes_in_buffer -= size_t(num_bytes); + } + } + + static void do_term(j_decompress_ptr cinfo) + { + } + + util::core_file *infile; + JOCTET *buffer; + bool start_of_file; +}; + +void jpeg_corefile_source::source(j_decompress_ptr cinfo, util::core_file &file) +{ + jpeg_corefile_source *src; + if (!cinfo->src) + { + src = reinterpret_cast<jpeg_corefile_source *>( + (*cinfo->mem->alloc_small)( + reinterpret_cast<j_common_ptr>(cinfo), + JPOOL_PERMANENT, + sizeof(jpeg_corefile_source))); + cinfo->src = src; + src->buffer = reinterpret_cast<JOCTET *>( + (*cinfo->mem->alloc_small)( + reinterpret_cast<j_common_ptr>(cinfo), + JPOOL_PERMANENT, + INPUT_BUF_SIZE * sizeof(JOCTET))); + } + else + { + src = static_cast<jpeg_corefile_source *>(cinfo->src); + } + + src->init_source = &jpeg_corefile_source::do_init; + src->fill_input_buffer = &jpeg_corefile_source::do_fill; + src->skip_input_data = &jpeg_corefile_source::do_skip; + src->resync_to_restart = jpeg_resync_to_restart; + src->term_source = &jpeg_corefile_source::do_term; + src->infile = &file; + src->bytes_in_buffer = 0; + src->next_input_byte = nullptr; +} + +} // anonymous namespace /*************************************************************************** @@ -23,7 +123,7 @@ /* utilities */ static void resample_argb_bitmap_average(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy); static void resample_argb_bitmap_bilinear(u32 *dest, u32 drowpixels, u32 dwidth, u32 dheight, const u32 *source, u32 srowpixels, u32 swidth, u32 sheight, const render_color &color, u32 dx, u32 dy); -static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png); +static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const util::png_info &png); @@ -543,81 +643,103 @@ void render_line_to_quad(const render_bounds *bounds, float width, float length_ /*------------------------------------------------- - render_load_jpeg - load a JPG file into a - bitmap + render_load_msdib - load a Microsoft DIB file + into a bitmap -------------------------------------------------*/ -void render_load_jpeg(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, const char *filename) +void render_load_msdib(bitmap_argb32 &bitmap, util::core_file &file) { // deallocate previous bitmap bitmap.reset(); - // define file's full name - std::string fname; + // read the DIB data + util::msdib_error const result = util::msdib_read_bitmap(file, bitmap); + if (result != util::msdib_error::NONE) + { + osd_printf_error("Error reading Microsoft DIB file\n"); + bitmap.reset(); + } +} - if (dirname == nullptr) - fname = filename; - else - fname.assign(dirname).append(PATH_SEPARATOR).append(filename); - if (file.open(fname) != osd_file::error::NONE) - return; +/*------------------------------------------------- + render_load_jpeg - load a JPEG file into a + bitmap +-------------------------------------------------*/ + +void render_load_jpeg(bitmap_argb32 &bitmap, util::core_file &file) +{ + // deallocate previous bitmap + bitmap.reset(); - // define standard JPEG structures + // create a JPEG source for the file jpeg_decompress_struct cinfo; jpeg_error_mgr jerr; cinfo.err = jpeg_std_error(&jerr); - jpeg_create_decompress(&cinfo); - - // allocates a buffer for the image - u32 jpg_size = file.size(); - std::unique_ptr<unsigned char[]> jpg_buffer = std::make_unique<unsigned char[]>(jpg_size); - - // read data from the file and set them in the buffer - file.read(jpg_buffer.get(), jpg_size); - jpeg_mem_src(&cinfo, jpg_buffer.get(), jpg_size); - - // 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); - - // allocates a buffer to receive the information and copy them into the bitmap - int row_stride = cinfo.output_width * cinfo.output_components; - JSAMPARRAY buffer = (JSAMPARRAY)malloc(sizeof(JSAMPROW)); - buffer[0] = (JSAMPROW)malloc(sizeof(JSAMPLE) * row_stride); - - while ( cinfo.output_scanline < cinfo.output_height ) + jerr.error_exit = [] (j_common_ptr cinfo) { throw cinfo->err; }; + JSAMPARRAY buffer = nullptr; + try { - int j = cinfo.output_scanline; - jpeg_read_scanlines(&cinfo, buffer, 1); + 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); + + // 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)); + + while (cinfo.output_scanline < cinfo.output_height) + { + 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]); + 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]); - 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 %s file.\n", fname); - bitmap.reset(); - break; + } + 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); + // finish decompression and frees the memory + jpeg_finish_decompress(&cinfo); + } + catch (jpeg_error_mgr *err) + { + 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(); + } jpeg_destroy_decompress(&cinfo); - file.close(); - free(buffer[0]); - free(buffer); + if (buffer) + { + if (buffer[0]) + free(buffer[0]); + free(buffer); + } } @@ -626,36 +748,25 @@ void render_load_jpeg(bitmap_argb32 &bitmap, emu_file &file, const char *dirname bitmap -------------------------------------------------*/ -bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, const char *filename, bool load_as_alpha_to_existing) +bool render_load_png(bitmap_argb32 &bitmap, util::core_file &file, bool load_as_alpha_to_existing) { // deallocate if we're not overlaying alpha if (!load_as_alpha_to_existing) bitmap.reset(); - // open the file - std::string fname; - if (dirname) - fname.assign(dirname).append(PATH_SEPARATOR).append(filename); - else - fname.assign(filename); - osd_file::error const filerr = file.open(fname); - if (filerr != osd_file::error::NONE) - return false; - // read the PNG data - png_info png; - png_error const result = png.read_file(file); - file.close(); - if (result != PNGERR_NONE) + util::png_info png; + util::png_error const result = png.read_file(file); + if (result != util::png_error::NONE) { - osd_printf_error("%s: Error reading PNG file\n", filename); + osd_printf_error("Error reading PNG file\n"); return false; } // if less than 8 bits, upsample - if (PNGERR_NONE != png.expand_buffer_8bit()) + if (util::png_error::NONE != png.expand_buffer_8bit()) { - osd_printf_error("%s: Error upsampling PNG bitmap\n", filename); + osd_printf_error("Error upsampling PNG bitmap\n"); return false; } @@ -663,9 +774,9 @@ bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, if (!load_as_alpha_to_existing) { // non-alpha case - if (PNGERR_NONE != png.copy_to_bitmap(bitmap, hasalpha)) + if (util::png_error::NONE != png.copy_to_bitmap(bitmap, hasalpha)) { - osd_printf_error("%s: Error copying PNG bitmap to MAME bitmap\n", filename); + osd_printf_error("Error copying PNG bitmap to MAME bitmap\n"); return false; } } @@ -674,7 +785,7 @@ bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, // verify we can handle this PNG if (png.bit_depth > 8) { - osd_printf_error("%s: Unsupported bit depth %d (8 bit max)\n", filename, png.bit_depth); + osd_printf_error("Unsupported bit depth %d (8 bit max)\n", png.bit_depth); return false; } @@ -692,7 +803,7 @@ bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, to the alpha channel of a bitmap -------------------------------------------------*/ -static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png) +static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const util::png_info &png) { // FIXME: this function is basically copy/pasted from the PNG code in util, and should be unified with it u8 accumalpha = 0xff; @@ -803,29 +914,41 @@ static bool copy_png_alpha_to_bitmap(bitmap_argb32 &bitmap, const png_info &png) render_detect_image - detect image format -------------------------------------------------*/ -ru_imgformat render_detect_image(emu_file &file, const char *dirname, const char *filename) +ru_imgformat render_detect_image(util::core_file &file) { - // open the file - std::string fname; - if (dirname) - fname.assign(dirname).append(PATH_SEPARATOR).append(filename); - else - fname.assign(filename); - osd_file::error const filerr = file.open(fname); - if (filerr != osd_file::error::NONE) - return RENDUTIL_IMGFORMAT_ERROR; - // PNG: check for valid header - png_error const result = png_info::verify_header(file); - if (result == PNGERR_NONE) - { - file.close(); + 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_create_decompress(&cinfo); + cinfo.mem->max_memory_to_use = 128 * 1024 * 1024; + jpeg_corefile_source::source(&cinfo, file); + jpeg_read_header(&cinfo, TRUE); + jpeg_destroy_decompress(&cinfo); + file.seek(0, SEEK_SET); + return RENDUTIL_IMGFORMAT_JPEG; + } + catch (jpeg_error_mgr *err) + { + 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); - // TODO: add more when needed + if (util::msdib_error::NONE == msdib) + return RENDUTIL_IMGFORMAT_MSDIB; - file.close(); + // TODO: add more as necessary return RENDUTIL_IMGFORMAT_UNKNOWN; } |