summaryrefslogtreecommitdiffstatshomepage
path: root/src/lib
diff options
context:
space:
mode:
author Vas Crabb <vas@vastheman.com>2020-10-08 02:04:31 +1100
committer Vas Crabb <vas@vastheman.com>2020-10-08 02:04:31 +1100
commitb90fd355150a0f56e368bb33619459b19ec92bee (patch)
tree41ac320a4199b0856a485c9d75458dbc98aa168e /src/lib
parent4b0903bfdbe88d4e220fc2c2f140e7b503060909 (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/lib')
-rw-r--r--src/lib/util/msdib.cpp642
-rw-r--r--src/lib/util/msdib.h46
-rw-r--r--src/lib/util/nanosvg.h33
-rw-r--r--src/lib/util/png.cpp253
-rw-r--r--src/lib/util/png.h42
5 files changed, 867 insertions, 149 deletions
diff --git a/src/lib/util/msdib.cpp b/src/lib/util/msdib.cpp
new file mode 100644
index 00000000000..a1ae5eaddf0
--- /dev/null
+++ b/src/lib/util/msdib.cpp
@@ -0,0 +1,642 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ msdib.h
+
+ Microsoft Device-Independent Bitmap file loading.
+
+***************************************************************************/
+
+#include "msdib.h"
+
+#include "eminline.h"
+
+#include <cassert>
+#include <cstdlib>
+#include <cstring>
+
+
+#define LOG_GENERAL (1U << 0)
+#define LOG_DIB (1U << 1)
+
+//#define VERBOSE (LOG_GENERAL | LOG_DIB)
+
+#define LOG_OUTPUT_FUNC osd_printf_verbose
+
+#ifndef VERBOSE
+#define VERBOSE 0
+#endif
+
+#define LOGMASKED(mask, ...) do { if (VERBOSE & (mask)) (LOG_OUTPUT_FUNC)(__VA_ARGS__); } while (false)
+
+#define LOG(...) LOGMASKED(LOG_GENERAL, __VA_ARGS__)
+
+
+namespace util {
+
+namespace {
+
+// DIB compression schemes
+enum : std::uint32_t
+{
+ DIB_COMP_NONE = 0,
+ DIB_COMP_RLE8 = 1,
+ DIB_COMP_RLE4 = 2,
+ DIB_COMP_BITFIELDS = 3
+};
+
+
+// file header doesn't use natural alignment
+using bitmap_file_header = std::uint8_t [14];
+
+
+// old-style DIB header
+struct bitmap_core_header
+{
+ std::uint32_t size; // size of the header (12, 16 or 64)
+ std::int16_t width; // width of bitmap in pixels
+ std::int16_t height; // height of the image in pixels
+ std::uint16_t planes; // number of colour planes (must be 1)
+ std::uint16_t bpp; // bits per pixel
+};
+
+// new-style DIB header
+struct bitmap_info_header
+{
+ std::uint32_t size; // size of the header
+ std::int32_t width; // width of bitmap in pixels
+ std::int32_t height; // height of bitmap in pixels
+ std::uint16_t planes; // number of colour planes (must be 1)
+ std::uint16_t bpp; // bits per pixel
+ std::uint32_t comp; // compression method
+ std::uint32_t rawsize; // size of bitmap data after decompression or 0 if uncompressed
+ std::int32_t hres; // horizontal resolution in pixels/metre
+ std::int32_t vres; // horizontal resolution in pixels/metre
+ std::uint32_t colors; // number of colours or 0 for 1 << bpp
+ std::uint32_t important; // number of important colours or 0 if all important
+ std::uint32_t red; // red field mask - must be contiguous
+ std::uint32_t green; // green field mask - must be contiguous
+ std::uint32_t blue; // blue field mask - must be contiguous
+ std::uint32_t alpha; // alpha field mask - must be contiguous
+};
+
+
+union bitmap_headers
+{
+ bitmap_core_header core;
+ bitmap_info_header info;
+};
+
+
+bool dib_parse_mask(uint32_t mask, unsigned &shift, unsigned &bits)
+{
+ shift = count_leading_zeros(mask);
+ mask <<= shift;
+ bits = count_leading_ones(mask);
+ mask <<= shift;
+ shift = 32 - shift - bits;
+ return !mask;
+}
+
+
+void dib_truncate_channel(unsigned &shift, unsigned &bits)
+{
+ if (8U < bits)
+ {
+ unsigned const excess(bits - 8);
+ shift += excess;
+ bits -= excess;
+ }
+}
+
+
+uint8_t dib_splat_sample(uint8_t val, unsigned bits)
+{
+ assert(8U >= bits);
+ for (val <<= (8U - bits); bits && (8U > bits); bits <<= 1)
+ val |= val >> bits;
+ return val;
+}
+
+
+msdib_error dib_read_file_header(core_file &fp, std::uint32_t &filelen)
+{
+ // the bitmap file header doesn't use natural alignment
+ bitmap_file_header file_header;
+ if (fp.read(file_header, sizeof(file_header)) != sizeof(file_header))
+ {
+ LOG("Error reading DIB file header\n");
+ return msdib_error::FILE_TRUNCATED;
+ }
+
+ // only support Windows bitmaps for now
+ if ((0x42 != file_header[0]) || (0x4d != file_header[1]))
+ return msdib_error::BAD_SIGNATURE;
+
+ // do a very basic check on the file length
+ std::uint32_t const file_length(
+ (std::uint32_t(file_header[2]) << 0) |
+ (std::uint32_t(file_header[3]) << 8) |
+ (std::uint32_t(file_header[4]) << 16) |
+ (std::uint32_t(file_header[5]) << 24));
+ if ((sizeof(file_header) + sizeof(bitmap_core_header)) > file_length)
+ return msdib_error::FILE_CORRUPT;
+
+ // check that the offset to the pixel data looks half sane
+ std::uint32_t const pixel_offset(
+ (std::uint32_t(file_header[10]) << 0) |
+ (std::uint32_t(file_header[11]) << 8) |
+ (std::uint32_t(file_header[12]) << 16) |
+ (std::uint32_t(file_header[13]) << 24));
+ if (((sizeof(file_header) + sizeof(bitmap_core_header)) > pixel_offset) || (file_length < pixel_offset))
+ return msdib_error::FILE_CORRUPT;
+
+ // looks OK enough
+ filelen = file_length;
+ return msdib_error::NONE;
+}
+
+
+msdib_error dib_read_bitmap_header(
+ core_file &fp,
+ bitmap_headers &header,
+ unsigned &palette_bytes,
+ bool &indexed,
+ std::size_t &palette_entries,
+ std::size_t &palette_size,
+ std::size_t &row_bytes,
+ std::uint32_t length)
+{
+ // check that these things haven't been padded somehow
+ static_assert(sizeof(bitmap_core_header) == 12U, "compiler has applied padding to bitmap_core_header");
+ static_assert(sizeof(bitmap_info_header) == 56U, "compiler has applied padding to bitmap_info_header");
+
+ // ensure the header fits in the space for the image data
+ assert(&header.core.size == &header.info.size);
+ if (sizeof(header.core) > length)
+ return msdib_error::FILE_TRUNCATED;
+ std::memset(&header, 0, sizeof(header));
+ if (fp.read(&header.core.size, sizeof(header.core.size)) != sizeof(header.core.size))
+ {
+ LOG("Error reading DIB header size (length %u)\n", length);
+ return msdib_error::FILE_TRUNCATED;
+ }
+ header.core.size = little_endianize_int32(header.core.size);
+ if (length < header.core.size)
+ {
+ LOG("DIB image data (%u bytes) is too small for DIB header (%u bytes)\n", length, header.core.size);
+ return msdib_error::FILE_CORRUPT;
+ }
+
+ // identify and read the header - convert OS/2 headers to Windows 3 format
+ palette_bytes = 4U;
+ switch (header.core.size)
+ {
+ case 16U:
+ case 64U:
+ // extended OS/2 bitmap header with support for compression
+ LOG(
+ "DIB image data (%u bytes) uses unsupported OS/2 DIB header (size %u)\n",
+ length,
+ header.core.size);
+ return msdib_error::UNSUPPORTED_FORMAT;
+
+ case 12U:
+ // introduced in OS/2 and Windows 2.0
+ {
+ palette_bytes = 3U;
+ std::uint32_t const header_read(std::min<std::uint32_t>(header.core.size, sizeof(header.core)) - sizeof(header.core.size));
+ if (fp.read(&header.core.width, header_read) != header_read)
+ {
+ LOG("Error reading DIB core header from image data (%u bytes)\n", length);
+ return msdib_error::FILE_TRUNCATED;
+ }
+ fp.seek(header.core.size - sizeof(header.core.size) - header_read, SEEK_CUR);
+ header.core.width = little_endianize_int16(header.core.width);
+ header.core.height = little_endianize_int16(header.core.height);
+ header.core.planes = little_endianize_int16(header.core.planes);
+ header.core.bpp = little_endianize_int16(header.core.bpp);
+ LOGMASKED(
+ LOG_DIB,
+ "Read DIB core header from image data : %d*%d, %u planes, %u bpp\n",
+ header.core.width,
+ header.core.height,
+ header.core.planes,
+ header.core.bpp);
+
+ // this works because the core header only aliases the width/height of the info header
+ header.info.bpp = header.core.bpp;
+ header.info.planes = header.core.planes;
+ header.info.height = header.core.height;
+ header.info.width = header.core.width;
+ header.info.size = 40U;
+ }
+ break;
+
+ default:
+ // the next version will be longer
+ if (124U >= header.core.size)
+ {
+ LOG(
+ "DIB image data (%u bytes) uses unsupported DIB header format (size %u)\n",
+ length,
+ header.core.size);
+ return msdib_error::UNSUPPORTED_FORMAT;
+ }
+ // fall through
+ case 40U:
+ case 52U:
+ case 56U:
+ case 108U:
+ case 124U:
+ // the Windows 3 bitmap header with optional extensions
+ {
+ palette_bytes = 4U;
+ std::uint32_t const header_read(std::min<std::uint32_t>(header.info.size, sizeof(header.info)) - sizeof(header.info.size));
+ if (fp.read(&header.info.width, header_read) != header_read)
+ {
+ LOG("Error reading DIB info header from image data (%u bytes)\n", length);
+ return msdib_error::FILE_TRUNCATED;
+ }
+ fp.seek(header.info.size - sizeof(header.info.size) - header_read, SEEK_CUR);
+ header.info.width = little_endianize_int32(header.info.width);
+ header.info.height = little_endianize_int32(header.info.height);
+ header.info.planes = little_endianize_int16(header.info.planes);
+ header.info.bpp = little_endianize_int16(header.info.bpp);
+ header.info.comp = little_endianize_int32(header.info.comp);
+ header.info.rawsize = little_endianize_int32(header.info.rawsize);
+ header.info.hres = little_endianize_int32(header.info.hres);
+ header.info.vres = little_endianize_int32(header.info.vres);
+ header.info.colors = little_endianize_int32(header.info.colors);
+ header.info.important = little_endianize_int32(header.info.important);
+ header.info.red = little_endianize_int32(header.info.red);
+ header.info.green = little_endianize_int32(header.info.green);
+ header.info.blue = little_endianize_int32(header.info.blue);
+ header.info.alpha = little_endianize_int32(header.info.alpha);
+ LOGMASKED(
+ LOG_DIB,
+ "Read DIB info header from image data: %d*%d (%d*%d ppm), %u planes, %u bpp %u/%s%u colors\n",
+ header.info.width,
+ header.info.height,
+ header.info.hres,
+ header.info.vres,
+ header.info.planes,
+ header.info.bpp,
+ header.info.important,
+ header.info.colors ? "" : "2^",
+ header.info.colors ? header.info.colors : header.info.bpp);
+ }
+ break;
+ }
+
+ // check for unsupported planes/bit depth
+ if ((1U != header.info.planes) || !header.info.bpp || (32U < header.info.bpp) || ((8U < header.info.bpp) ? (header.info.bpp % 8) : (8 % header.info.bpp)))
+ {
+ LOG("DIB image data uses unsupported planes/bits per pixel %u*%u\n", header.info.planes, header.info.bpp);
+ return msdib_error::UNSUPPORTED_FORMAT;
+ }
+
+ // check dimensions
+ if ((0 >= header.info.width) || (0 == header.info.height))
+ {
+ LOG("DIB image data has invalid dimensions %u*%u\n", header.info.width, header.info.height);
+ return msdib_error::FILE_CORRUPT;
+ }
+
+ // ensure compression scheme is supported
+ bool no_palette(false);
+ indexed = true;
+ switch (header.info.comp)
+ {
+ case DIB_COMP_NONE:
+ // uncompressed - direct colour with implied bitfields if more than eight bits/pixel
+ indexed = 8U >= header.info.bpp;
+ if (indexed)
+ {
+ if ((1U << header.info.bpp) < header.info.colors)
+ {
+ osd_printf_verbose(
+ "DIB image data has oversized palette with %u entries for %u bits per pixel\n",
+ header.info.colors,
+ header.info.bpp);
+ }
+ }
+ if (!indexed)
+ {
+ no_palette = true;
+ switch(header.info.bpp)
+ {
+ case 16U:
+ header.info.red = 0x00007c00;
+ header.info.green = 0x000003e0;
+ header.info.blue = 0x0000001f;
+ header.info.alpha = 0x00000000;
+ break;
+ case 24U:
+ case 32U:
+ header.info.red = 0x00ff0000;
+ header.info.green = 0x0000ff00;
+ header.info.blue = 0x000000ff;
+ header.info.alpha = 0x00000000;
+ break;
+ }
+ }
+ break;
+
+ case DIB_COMP_BITFIELDS:
+ // uncompressed direct colour with explicitly-specified bitfields
+ indexed = false;
+ if (offsetof(bitmap_info_header, alpha) > header.info.size)
+ {
+ osd_printf_verbose(
+ "DIB image data specifies bit masks but is too small (size %u)\n",
+ header.info.size);
+ return msdib_error::FILE_CORRUPT;
+ }
+ break;
+
+ default:
+ LOG("DIB image data uses unsupported compression scheme %u\n", header.info.comp);
+ return msdib_error::UNSUPPORTED_FORMAT;
+ }
+
+ // we can now calculate the size of the palette and row data
+ palette_entries =
+ indexed
+ ? ((1U == header.info.bpp) ? 2U : header.info.colors ? header.info.colors : (1U << header.info.bpp))
+ : (no_palette ? 0U : header.info.colors);
+ palette_size = palette_bytes * palette_entries;
+ row_bytes = ((31 + (header.info.width * header.info.bpp)) >> 5) << 2;
+
+ // header looks OK
+ return msdib_error::NONE;
+}
+
+} // anonymous namespace
+
+
+
+msdib_error msdib_verify_header(core_file &fp)
+{
+ msdib_error err;
+
+ // file header
+ std::uint32_t file_length;
+ err = dib_read_file_header(fp, file_length);
+ if (msdib_error::NONE != err)
+ return err;
+
+ // bitmap header
+ bitmap_headers header;
+ unsigned palette_bytes;
+ bool indexed;
+ std::size_t palette_entries, palette_size, row_bytes;
+ err = dib_read_bitmap_header(
+ fp,
+ header,
+ palette_bytes,
+ indexed,
+ palette_entries,
+ palette_size,
+ row_bytes,
+ file_length - sizeof(bitmap_file_header));
+ if (msdib_error::NONE != err)
+ return err;
+
+ // check length
+ std::size_t const required_size(
+ sizeof(bitmap_file_header) +
+ header.info.size +
+ palette_size +
+ (row_bytes * std::abs(header.info.height)));
+ if (required_size > file_length)
+ return msdib_error::FILE_TRUNCATED;
+
+ // good chance this file is supported
+ return msdib_error::NONE;
+}
+
+
+msdib_error msdib_read_bitmap(core_file &fp, bitmap_argb32 &bitmap)
+{
+ std::uint32_t file_length;
+ msdib_error const headerr(dib_read_file_header(fp, file_length));
+ if (msdib_error::NONE != headerr)
+ return headerr;
+
+ return msdib_read_bitmap_data(fp, bitmap, file_length - sizeof(bitmap_file_header), 0U);
+}
+
+
+msdib_error msdib_read_bitmap_data(core_file &fp, bitmap_argb32 &bitmap, std::uint32_t length, std::uint32_t dirheight)
+{
+ // read the bitmap header
+ bitmap_headers header;
+ unsigned palette_bytes;
+ bool indexed;
+ std::size_t palette_entries, palette_size, row_bytes;
+ msdib_error const head_error(dib_read_bitmap_header(fp, header, palette_bytes, indexed, palette_entries, palette_size, row_bytes, length));
+ if (msdib_error::NONE != head_error)
+ return head_error;
+
+ // check dimensions
+ bool const top_down(0 > header.info.height);
+ if (top_down)
+ header.info.height = -header.info.height;
+ bool have_and_mask((2 * dirheight) == header.info.height);
+ if (!have_and_mask && (0 < dirheight) && (dirheight != header.info.height))
+ {
+ osd_printf_verbose(
+ "DIB image data height %d doesn't match directory height %u with or without AND mask\n",
+ header.info.height,
+ dirheight);
+ return msdib_error::UNSUPPORTED_FORMAT;
+ }
+ if (have_and_mask)
+ header.info.height >>= 1;
+
+ // we can now calculate the size of the image data
+ std::size_t const mask_row_bytes(((31 + header.info.width) >> 5) << 2);
+ std::size_t const required_size(
+ header.info.size +
+ palette_size +
+ ((row_bytes + (have_and_mask ? mask_row_bytes : 0U)) * header.info.height));
+ if (required_size > length)
+ {
+ LOG("DIB image data (%u bytes) smaller than calculated DIB data size (%u bytes)\n", length, required_size);
+ return msdib_error::FILE_TRUNCATED;
+ }
+
+ // load the palette for indexed colour formats or the shifts for direct colour formats
+ unsigned red_shift(0), green_shift(0), blue_shift(0), alpha_shift(0);
+ unsigned red_bits(0), green_bits(0), blue_bits(0), alpha_bits(0);
+ std::unique_ptr<rgb_t []> palette;
+ if (indexed)
+ {
+ // read palette and convert
+ std::unique_ptr<std::uint8_t []> palette_data;
+ try { palette_data.reset(new std::uint8_t [palette_size]); }
+ catch (std::bad_alloc const &) { return msdib_error::OUT_OF_MEMORY; }
+ if (fp.read(palette_data.get(), palette_size) != palette_size)
+ {
+ LOG("Error reading palette from DIB image data (%u bytes)\n", length);
+ return msdib_error::FILE_TRUNCATED;
+ }
+ std::size_t const palette_usable(std::min<std::size_t>(palette_entries, std::size_t(1) << header.info.bpp));
+ try { palette.reset(new rgb_t [palette_usable]); }
+ catch (std::bad_alloc const &) { return msdib_error::OUT_OF_MEMORY; }
+ std::uint8_t const *ptr(palette_data.get());
+ for (std::size_t i = 0; palette_usable > i; ++i, ptr += palette_bytes)
+ palette[i] = rgb_t(ptr[2], ptr[1], ptr[0]);
+ }
+ else
+ {
+ // skip over the palette if necessary
+ if (palette_entries)
+ fp.seek(palette_bytes * palette_entries, SEEK_CUR);
+
+ // convert masks to shifts
+ bool const masks_contiguous(
+ dib_parse_mask(header.info.red, red_shift, red_bits) &&
+ dib_parse_mask(header.info.green, green_shift, green_bits) &&
+ dib_parse_mask(header.info.blue, blue_shift, blue_bits) &&
+ dib_parse_mask(header.info.alpha, alpha_shift, alpha_bits));
+ if (!masks_contiguous)
+ {
+ osd_printf_verbose(
+ "DIB image data specifies non-contiguous channel masks 0x%x | 0x%x | 0x%x | 0x%x\n",
+ header.info.red,
+ header.info.green,
+ header.info.blue,
+ header.info.alpha);
+ }
+ if ((32U != header.info.bpp) && ((header.info.red | header.info.green | header.info.blue | header.info.alpha) >> header.info.bpp))
+ {
+ LOG(
+ "DIB image data specifies channel masks 0x%x | 0x%x | 0x%x | 0x%x that exceed %u bits per pixel\n",
+ header.info.red,
+ header.info.green,
+ header.info.blue,
+ header.info.alpha,
+ header.info.bpp);
+ return msdib_error::FILE_CORRUPT;
+ }
+ LOGMASKED(
+ LOG_DIB,
+ "DIB image data using channels: R((x >> %2$u) & 0x%3$0*1$x) G((x >> %4$u) & 0x%5$0*1$x) B((x >> %6$u) & 0x%7$0*1$x) A((x >> %8$u) & 0x%9$0*1$x)\n",
+ (header.info.bpp + 3) >> 2,
+ red_shift,
+ (std::uint32_t(1) << red_bits) - 1,
+ green_shift,
+ (std::uint32_t(1) << green_bits) - 1,
+ blue_shift,
+ (std::uint32_t(1) << blue_bits) - 1,
+ alpha_shift,
+ (std::uint32_t(1) << alpha_bits) - 1);
+
+ // the MAME bitmap only supports 8 bits/sample maximum
+ dib_truncate_channel(red_shift, red_bits);
+ dib_truncate_channel(green_shift, green_bits);
+ dib_truncate_channel(blue_shift, blue_bits);
+ dib_truncate_channel(alpha_shift, alpha_bits);
+ }
+
+ // allocate the bitmap and process row data
+ std::unique_ptr<std::uint8_t []> row_data;
+ try {row_data.reset(new std::uint8_t [row_bytes]); }
+ catch (std::bad_alloc const &) { return msdib_error::OUT_OF_MEMORY; }
+ bitmap.allocate(header.info.width, header.info.height);
+ int const y_inc(top_down ? 1 : -1);
+ for (std::int32_t i = 0, y = top_down ? 0 : (header.info.height - 1); header.info.height > i; ++i, y += y_inc)
+ {
+ if (fp.read(row_data.get(), row_bytes) != row_bytes)
+ {
+ LOG("Error reading DIB row %d data from image data\n", i);
+ return msdib_error::FILE_TRUNCATED;
+ }
+ std::uint8_t *src(row_data.get());
+ std::uint32_t *dest(&bitmap.pix(y));
+ unsigned shift(0U);
+ for (std::int32_t x = 0; header.info.width > x; ++x, ++dest)
+ {
+ // extract or compose a pixel
+ std::uint32_t pix(0U);
+ if (8U >= header.info.bpp)
+ {
+ assert(8U > shift);
+ pix = *src >> (8U - header.info.bpp);
+ *src <<= header.info.bpp;
+ shift += header.info.bpp;
+ if (8U <= shift)
+ {
+ shift = 0U;
+ ++src;
+ }
+ }
+ else for (shift = 0; header.info.bpp > shift; shift += 8U, ++src)
+ {
+ pix |= std::uint32_t(*src) << shift;
+ }
+
+ // convert to RGB
+ if (indexed)
+ {
+ if (palette_entries > pix)
+ {
+ *dest = palette[pix];
+ }
+ else
+ {
+ *dest = rgb_t::transparent();
+ osd_printf_verbose(
+ "DIB image data has out-of-range color %u at (%d, %d) with %u palette entries\n",
+ pix,
+ x,
+ y,
+ palette_entries);
+ }
+ }
+ else
+ {
+ std::uint8_t r(dib_splat_sample((pix >> red_shift) & ((std::uint32_t(1) << red_bits) - 1), red_bits));
+ std::uint8_t g(dib_splat_sample((pix >> green_shift) & ((std::uint32_t(1) << green_bits) - 1), green_bits));
+ std::uint8_t b(dib_splat_sample((pix >> blue_shift) & ((std::uint32_t(1) << blue_bits) - 1), blue_bits));
+ std::uint8_t a(dib_splat_sample((pix >> alpha_shift) & ((std::uint32_t(1) << alpha_bits) - 1), alpha_bits));
+ *dest = rgb_t(alpha_bits ? a : 255, r, g, b);
+ }
+ }
+ }
+
+ // process the AND mask if present
+ if (have_and_mask)
+ {
+ for (std::int32_t i = 0, y = top_down ? 0 : (header.info.height - 1); header.info.height > i; ++i, y += y_inc)
+ {
+ if (fp.read(row_data.get(), mask_row_bytes) != mask_row_bytes)
+ {
+ LOG("Error reading DIB mask row %d data from image data\n", i);
+ return msdib_error::FILE_TRUNCATED;
+ }
+ std::uint8_t *src(row_data.get());
+ std::uint32_t *dest(&bitmap.pix(y));
+ unsigned shift(0U);
+ for (std::int32_t x = 0; header.info.width > x; ++x, ++dest)
+ {
+ assert(8U > shift);
+ rgb_t pix(*dest);
+ *dest = pix.set_a(BIT(*src, 7U - shift) ? 0U : pix.a());
+ if (8U <= ++shift)
+ {
+ shift = 0U;
+ ++src;
+ }
+ }
+ }
+ }
+
+ // we're done!
+ return msdib_error::NONE;
+}
+
+} // namespace util
diff --git a/src/lib/util/msdib.h b/src/lib/util/msdib.h
new file mode 100644
index 00000000000..54767e216fb
--- /dev/null
+++ b/src/lib/util/msdib.h
@@ -0,0 +1,46 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ msdib.h
+
+ Microsoft Device-Independent Bitmap file loading.
+
+***************************************************************************/
+#ifndef MAME_LIB_UTIL_MSDIB_H
+#define MAME_LIB_UTIL_MSDIB_H
+
+#pragma once
+
+#include "bitmap.h"
+#include "corefile.h"
+#include "osdcore.h"
+
+#include <cstdint>
+
+
+namespace util {
+
+/***************************************************************************
+ CONSTANTS
+***************************************************************************/
+
+// Error types
+enum class msdib_error
+{
+ NONE,
+ OUT_OF_MEMORY,
+ FILE_ERROR,
+ BAD_SIGNATURE,
+ FILE_TRUNCATED,
+ FILE_CORRUPT,
+ UNSUPPORTED_FORMAT
+};
+
+msdib_error msdib_verify_header(core_file &fp);
+msdib_error msdib_read_bitmap(core_file &fp, bitmap_argb32 &bitmap);
+msdib_error msdib_read_bitmap_data(core_file &fp, bitmap_argb32 &bitmap, std::uint32_t length, std::uint32_t dirheight = 0U);
+
+} // namespace util
+
+#endif // MAME_LIB_UTIL_MSDIB_H
diff --git a/src/lib/util/nanosvg.h b/src/lib/util/nanosvg.h
new file mode 100644
index 00000000000..6be55cdae32
--- /dev/null
+++ b/src/lib/util/nanosvg.h
@@ -0,0 +1,33 @@
+// license:BSD-3-Clause
+// copyright-holders:Vas Crabb
+/***************************************************************************
+
+ nanosvg.h
+
+ NanoSVG helpers.
+
+***************************************************************************/
+#ifndef MAME_LIB_UTIL_NANOSVG_H
+#define MAME_LIB_UTIL_NANOSVG_H
+
+#include <nanosvg/src/nanosvg.h>
+#include <nanosvg/src/nanosvgrast.h>
+
+#include <memory>
+
+
+namespace util {
+
+struct nsvg_deleter
+{
+ void operator()(NSVGimage *ptr) { nsvgDelete(ptr); }
+ void operator()(NSVGrasterizer *ptr) { nsvgDeleteRasterizer(ptr); }
+};
+
+
+using nsvg_image_ptr = std::unique_ptr<NSVGimage, nsvg_deleter>;
+using nsvg_rasterizer_ptr = std::unique_ptr<NSVGrasterizer, nsvg_deleter>;
+
+} // namespace util
+
+#endif // MAME_LIB_UTIL_NANOSVG_H
diff --git a/src/lib/util/png.cpp b/src/lib/util/png.cpp
index 69835e54051..50aeb28c5fe 100644
--- a/src/lib/util/png.cpp
+++ b/src/lib/util/png.cpp
@@ -2,7 +2,7 @@
// copyright-holders:Aaron Giles, Vas Crabb
/*********************************************************************
- png.c
+ png.cpp
PNG reading functions.
@@ -16,34 +16,13 @@
#include <algorithm>
#include <cassert>
-#include <cstdint>
-#include <cstring>
-#include <cmath>
-#include <new>
-
#include <cmath>
#include <cstdlib>
+#include <cstring>
+#include <new>
-
-/***************************************************************************
- GLOBAL VARIABLES
-***************************************************************************/
-
-static const int samples[] = { 1, 0, 3, 1, 2, 0, 4 };
-
-
-
-/***************************************************************************
- INLINE FUNCTIONS
-***************************************************************************/
-
-static inline int compute_rowbytes(const png_info &pnginfo)
-{
- return (pnginfo.width * samples[pnginfo.color_type] * pnginfo.bit_depth + 7) / 8;
-}
-
-
+namespace util {
/***************************************************************************
GENERAL FUNCTIONS
@@ -66,6 +45,12 @@ void png_info::free_data()
namespace {
+/***************************************************************************
+ GLOBAL VARIABLES
+***************************************************************************/
+
+constexpr int samples[] = { 1, 0, 3, 1, 2, 0, 4 };
+
constexpr std::uint8_t PNG_SIGNATURE[] = { 0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a };
#define MNG_Signature "\x8A\x4D\x4E\x47\x0D\x0A\x1A\x0A"
@@ -101,6 +86,12 @@ constexpr std::uint8_t PNG_PF_Average = 3;
constexpr std::uint8_t PNG_PF_Paeth = 4;
+/***************************************************************************
+ INLINE FUNCTIONS
+***************************************************************************/
+
+inline int compute_rowbytes(const png_info &pnginfo) { return (pnginfo.width * samples[pnginfo.color_type] * pnginfo.bit_depth + 7) / 8; }
+
inline uint8_t fetch_8bit(uint8_t const *v) { return *v; }
inline uint16_t fetch_16bit(uint8_t const *v) { return big_endianize_int16(*reinterpret_cast<uint16_t const *>(v)); }
inline uint32_t fetch_32bit(uint8_t const *v) { return big_endianize_int32(*reinterpret_cast<uint32_t const *>(v)); }
@@ -132,11 +123,11 @@ private:
{
// do some basic checks for unsupported images
if (!pnginfo.bit_depth || (ARRAY_LENGTH(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type])
- return PNGERR_UNSUPPORTED_FORMAT; // unknown colour format
+ return png_error::UNSUPPORTED_FORMAT; // unknown colour format
if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method))
- return PNGERR_UNSUPPORTED_FORMAT; // unknown interlace method
+ return png_error::UNSUPPORTED_FORMAT; // unknown interlace method
if ((3 == pnginfo.color_type) && (!pnginfo.num_palette || !pnginfo.palette))
- return PNGERR_FILE_CORRUPT; // indexed colour with no palette
+ return png_error::FILE_CORRUPT; // indexed colour with no palette
// calculate the offset for each pass of the interlace
unsigned const pass_count(get_pass_count());
@@ -146,13 +137,13 @@ private:
// allocate memory for the filtered image
try { pnginfo.image.reset(new std::uint8_t [pass_offset[pass_count]]); }
- catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; }
+ catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
// decompress image data
- png_error error = PNGERR_NONE;
+ png_error error = png_error::NONE;
error = decompress(idata, pass_offset[pass_count]);
std::uint32_t const bpp(get_bytes_per_pixel());
- for (unsigned pass = 0; (pass_count > pass) && (PNGERR_NONE == error); ++pass)
+ for (unsigned pass = 0; (pass_count > pass) && (png_error::NONE == error); ++pass)
{
// compute some basic parameters
std::pair<std::uint32_t, std::uint32_t> const dimensions(get_pass_dimensions(pass));
@@ -161,7 +152,7 @@ private:
// we de-filter in place, stripping the filter bytes off the rows
uint8_t *dst(&pnginfo.image[pass_offset[pass]]);
uint8_t const *src(dst);
- for (std::uint32_t y = 0; (dimensions.second > y) && (PNGERR_NONE == error); ++y)
+ for (std::uint32_t y = 0; (dimensions.second > y) && (png_error::NONE == error); ++y)
{
// first byte of each row is the filter type
uint8_t const filter(*src++);
@@ -172,7 +163,7 @@ private:
}
// if we errored, free the image data
- if (error != PNGERR_NONE)
+ if (error != png_error::NONE)
pnginfo.image.reset();
return error;
@@ -182,7 +173,7 @@ private:
{
// only deflate is permitted
if (0 != pnginfo.compression_method)
- return PNGERR_DECOMPRESS_ERROR;
+ return png_error::DECOMPRESS_ERROR;
// allocate zlib stream
z_stream stream;
@@ -195,7 +186,7 @@ private:
stream.next_in = Z_NULL;
zerr = inflateInit(&stream);
if (Z_OK != zerr)
- return PNGERR_DECOMPRESS_ERROR;
+ return png_error::DECOMPRESS_ERROR;
// decompress IDAT blocks
stream.next_out = pnginfo.image.get();
@@ -217,28 +208,28 @@ private:
// it's all good if we got end-of-stream or we have with no data remaining
if ((Z_OK == inflateEnd(&stream)) && ((Z_STREAM_END == zerr) || ((Z_OK == zerr) && (idata.end() == it) && !stream.avail_in)))
- return PNGERR_NONE;
+ return png_error::NONE;
else
- return PNGERR_DECOMPRESS_ERROR;
+ return png_error::DECOMPRESS_ERROR;
}
png_error unfilter_row(std::uint8_t type, uint8_t const *src, uint8_t *dst, uint8_t const *dstprev, int bpp, std::uint32_t rowbytes)
{
if (0 != pnginfo.filter_method)
- return PNGERR_UNKNOWN_FILTER;
+ return png_error::UNKNOWN_FILTER;
switch (type)
{
case PNG_PF_None: // no filter, just copy
std::copy_n(src, rowbytes, dst);
- return PNGERR_NONE;
+ return png_error::NONE;
case PNG_PF_Sub: // SUB = previous pixel
dst = std::copy_n(src, bpp, dst);
src += bpp;
for (std::uint32_t x = bpp; rowbytes > x; ++x, ++src, ++dst)
*dst = *src + dst[-bpp];
- return PNGERR_NONE;
+ return png_error::NONE;
case PNG_PF_Up: // UP = pixel above
if (dstprev)
@@ -250,7 +241,7 @@ private:
{
std::copy_n(src, rowbytes, dst);
}
- return PNGERR_NONE;
+ return png_error::NONE;
case PNG_PF_Average: // AVERAGE = average of pixel above and previous pixel
if (dstprev)
@@ -267,7 +258,7 @@ private:
for (std::uint32_t x = bpp; rowbytes > x; ++x, ++src, ++dst)
*dst = *src + (dst[-bpp] >> 1);
}
- return PNGERR_NONE;
+ return png_error::NONE;
case PNG_PF_Paeth: // PAETH = special filter
for (std::uint32_t x = 0; rowbytes > x; ++x, ++src, ++dst)
@@ -281,10 +272,10 @@ private:
int32_t const dc(std::abs(prediction - pc));
*dst = ((da <= db) && (da <= dc)) ? (*src + pa) : (db <= dc) ? (*src + pb) : (*src + pc);
}
- return PNGERR_NONE;
+ return png_error::NONE;
default: // unknown filter type
- return PNGERR_UNKNOWN_FILTER;
+ return png_error::UNKNOWN_FILTER;
}
}
@@ -294,7 +285,7 @@ private:
{
case PNG_CN_IHDR: // image header
if (13 > length)
- return PNGERR_FILE_CORRUPT;
+ return png_error::FILE_CORRUPT;
pnginfo.width = fetch_32bit(&data[0]);
pnginfo.height = fetch_32bit(&data[4]);
pnginfo.bit_depth = fetch_8bit(&data[8]);
@@ -307,31 +298,31 @@ private:
case PNG_CN_PLTE: // palette
pnginfo.num_palette = length / 3;
if ((length % 3) || ((3 == pnginfo.color_type) && ((1 << pnginfo.bit_depth) < pnginfo.num_palette)))
- return PNGERR_FILE_CORRUPT;
+ return png_error::FILE_CORRUPT;
pnginfo.palette = std::move(data);
break;
case PNG_CN_tRNS: // transparency information
if (((0 == pnginfo.color_type) && (2 > length)) || ((2 == pnginfo.color_type) && (6 > length)))
- return PNGERR_FILE_CORRUPT;
+ return png_error::FILE_CORRUPT;
pnginfo.num_trans = length;
pnginfo.trans = std::move(data);
break;
case PNG_CN_IDAT: // image data
try { idata.emplace_back(length, std::move(data)); }
- catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; }
+ catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
break;
case PNG_CN_gAMA: // gamma
if (4 > length)
- return PNGERR_FILE_CORRUPT;
+ return png_error::FILE_CORRUPT;
pnginfo.source_gamma = fetch_32bit(data.get()) / 100000.0;
break;
case PNG_CN_pHYs: // physical information
if (9 > length)
- return PNGERR_FILE_CORRUPT;
+ return png_error::FILE_CORRUPT;
pnginfo.xres = fetch_32bit(&data[0]);
pnginfo.yres = fetch_32bit(&data[4]);
pnginfo.resolution_unit = fetch_8bit(&data[8]);
@@ -361,17 +352,17 @@ private:
}
catch (std::bad_alloc const &)
{
- return PNGERR_OUT_OF_MEMORY;
+ return png_error::OUT_OF_MEMORY;
}
break;
/* anything else */
default:
if ((type & 0x20000000) == 0)
- return PNGERR_UNKNOWN_CHUNK;
+ return png_error::UNKNOWN_CHUNK;
break;
}
- return PNGERR_NONE;
+ return png_error::NONE;
}
unsigned get_pass_count() const
@@ -413,23 +404,23 @@ private:
return ((samples[pnginfo.color_type] * pnginfo.bit_depth) + 7) >> 3;
}
- static png_error read_chunk(util::core_file &fp, std::unique_ptr<std::uint8_t []> &data, std::uint32_t &type, std::uint32_t &length)
+ static png_error read_chunk(core_file &fp, std::unique_ptr<std::uint8_t []> &data, std::uint32_t &type, std::uint32_t &length)
{
std::uint8_t tempbuff[4];
/* fetch the length of this chunk */
if (fp.read(tempbuff, 4) != 4)
- return PNGERR_FILE_TRUNCATED;
+ return png_error::FILE_TRUNCATED;
length = fetch_32bit(tempbuff);
/* fetch the type of this chunk */
if (fp.read(tempbuff, 4) != 4)
- return PNGERR_FILE_TRUNCATED;
+ return png_error::FILE_TRUNCATED;
type = fetch_32bit(tempbuff);
/* stop when we hit an IEND chunk */
if (type == PNG_CN_IEND)
- return PNGERR_NONE;
+ return png_error::NONE;
/* start the CRC with the chunk type (but not the length) */
std::uint32_t crc = crc32(0, tempbuff, 4);
@@ -439,13 +430,13 @@ private:
{
/* allocate memory for this chunk */
try { data.reset(new std::uint8_t [length]); }
- catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; }
+ catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
/* read the data from the file */
if (fp.read(data.get(), length) != length)
{
data.reset();
- return PNGERR_FILE_TRUNCATED;
+ return png_error::FILE_TRUNCATED;
}
/* update the CRC */
@@ -456,7 +447,7 @@ private:
if (fp.read(tempbuff, 4) != 4)
{
data.reset();
- return PNGERR_FILE_TRUNCATED;
+ return png_error::FILE_TRUNCATED;
}
std::uint32_t const chunk_crc = fetch_32bit(tempbuff);
@@ -464,10 +455,10 @@ private:
if (crc != chunk_crc)
{
data.reset();
- return PNGERR_FILE_CORRUPT;
+ return png_error::FILE_CORRUPT;
}
- return PNGERR_NONE;
+ return png_error::NONE;
}
png_info & pnginfo;
@@ -481,13 +472,13 @@ public:
{
// do some basic checks for unsupported images
if ((8 > pnginfo.bit_depth) || (pnginfo.bit_depth % 8))
- return PNGERR_UNSUPPORTED_FORMAT; // only do multiples of 8bps here - expand lower bit depth first
+ return png_error::UNSUPPORTED_FORMAT; // only do multiples of 8bps here - expand lower bit depth first
if ((ARRAY_LENGTH(samples) <= pnginfo.color_type) || !samples[pnginfo.color_type])
- return PNGERR_UNSUPPORTED_FORMAT; // unknown colour sample format
+ return png_error::UNSUPPORTED_FORMAT; // unknown colour sample format
if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method))
- return PNGERR_UNSUPPORTED_FORMAT; // unknown interlace method
+ return png_error::UNSUPPORTED_FORMAT; // unknown interlace method
if ((3 == pnginfo.color_type) && (8 != pnginfo.bit_depth))
- return PNGERR_UNSUPPORTED_FORMAT; // indexed colour must be exactly 8bpp
+ return png_error::UNSUPPORTED_FORMAT; // indexed colour must be exactly 8bpp
// everything looks sane, allocate the bitmap and deinterlace into it
bitmap.allocate(pnginfo.width, pnginfo.height);
@@ -603,22 +594,22 @@ public:
// set hasalpha flag and return
hasalpha = 0xffU != accumalpha;
- return PNGERR_NONE;
+ return png_error::NONE;
}
png_error expand_buffer_8bit()
{
// nothing to do if we're at 8 or greater already
if (pnginfo.bit_depth >= 8)
- return PNGERR_NONE;
+ return png_error::NONE;
// do some basic checks for unsupported images
if (!pnginfo.bit_depth || (8 % pnginfo.bit_depth))
- return PNGERR_UNSUPPORTED_FORMAT; // bit depth must be a factor of eight
+ return png_error::UNSUPPORTED_FORMAT; // bit depth must be a factor of eight
if ((0 != pnginfo.color_type) && (3 != pnginfo.color_type))
- return PNGERR_UNSUPPORTED_FORMAT; // only upsample monochrome and indexed colour
+ return png_error::UNSUPPORTED_FORMAT; // only upsample monochrome and indexed colour
if ((0 != pnginfo.interlace_method) && (1 != pnginfo.interlace_method))
- return PNGERR_UNSUPPORTED_FORMAT; // unknown interlace method
+ return png_error::UNSUPPORTED_FORMAT; // unknown interlace method
// calculate the offset for each pass of the interlace on the input and output
unsigned const pass_count(get_pass_count());
@@ -633,7 +624,7 @@ public:
// allocate a new buffer at 8-bit
std::unique_ptr<std::uint8_t []> outbuf;
try { outbuf.reset(new std::uint8_t [outp_offset[pass_count]]); }
- catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; }
+ catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
// upsample bitmap
std::uint8_t const bytesamples(8 / pnginfo.bit_depth);
@@ -687,13 +678,13 @@ public:
pnginfo.image = std::move(outbuf);
pnginfo.bit_depth = 8;
- return PNGERR_NONE;
+ return png_error::NONE;
}
- png_error read_file(util::core_file &fp)
+ png_error read_file(core_file &fp)
{
// initialize the data structures
- png_error error = PNGERR_NONE;
+ png_error error = png_error::NONE;
pnginfo.reset();
std::list<image_data_chunk> idata;
@@ -701,13 +692,13 @@ public:
error = verify_header(fp);
// loop until we hit an IEND chunk
- while (PNGERR_NONE == error)
+ while (png_error::NONE == error)
{
// read a chunk
std::unique_ptr<std::uint8_t []> chunk_data;
std::uint32_t chunk_type = 0, chunk_length;
error = read_chunk(fp, chunk_data, chunk_type, chunk_length);
- if (PNGERR_NONE == error)
+ if (png_error::NONE == error)
{
if (chunk_type == PNG_CN_IEND)
break; // stop when we hit an IEND chunk
@@ -717,29 +708,29 @@ public:
}
// finish processing the image
- if (PNGERR_NONE == error)
+ if (png_error::NONE == error)
error = process(idata);
// if we have an error, free all the output data
- if (error != PNGERR_NONE)
+ if (error != png_error::NONE)
pnginfo.reset();
return error;
}
- static png_error verify_header(util::core_file &fp)
+ static png_error verify_header(core_file &fp)
{
EQUIVALENT_ARRAY(PNG_SIGNATURE, std::uint8_t) signature;
// read 8 bytes
if (fp.read(signature, sizeof(signature)) != sizeof(signature))
- return PNGERR_FILE_TRUNCATED;
+ return png_error::FILE_TRUNCATED;
// return an error if we don't match
if (std::memcmp(signature, PNG_SIGNATURE, sizeof(PNG_SIGNATURE)))
- return PNGERR_BAD_SIGNATURE;
+ return png_error::BAD_SIGNATURE;
- return PNGERR_NONE;
+ return png_error::NONE;
}
};
@@ -760,7 +751,7 @@ constexpr unsigned png_private::ADAM7_Y_OFFS[7];
core stream
-------------------------------------------------*/
-png_error png_info::verify_header(util::core_file &fp)
+png_error png_info::verify_header(core_file &fp)
{
return png_private::verify_header(fp);
}
@@ -770,7 +761,7 @@ png_error png_info::verify_header(util::core_file &fp)
read_file - read a PNG from a core stream
-------------------------------------------------*/
-png_error png_info::read_file(util::core_file &fp)
+png_error png_info::read_file(core_file &fp)
{
return png_private(*this).read_file(fp);
}
@@ -781,7 +772,7 @@ png_error png_info::read_file(util::core_file &fp)
bitmap
-------------------------------------------------*/
-png_error png_read_bitmap(util::core_file &fp, bitmap_argb32 &bitmap)
+png_error png_read_bitmap(core_file &fp, bitmap_argb32 &bitmap)
{
png_error result;
png_info pnginfo;
@@ -789,12 +780,12 @@ png_error png_read_bitmap(util::core_file &fp, bitmap_argb32 &bitmap)
// read the PNG data
result = png.read_file(fp);
- if (PNGERR_NONE != result)
+ if (png_error::NONE != result)
return result;
// resample to 8bpp if necessary
result = png.expand_buffer_8bit();
- if (PNGERR_NONE != result)
+ if (png_error::NONE != result)
{
pnginfo.free_data();
return result;
@@ -848,13 +839,13 @@ png_error png_info::add_text(const char *keyword, const char *text)
char32_t ch;
int const len(uchar_from_utf8(&ch, ptr, kwend - ptr));
if ((0 >= len) || (32 > ch) || (255 < ch) || ((126 < ch) && (161 > ch)) || (((32 == prev) || (keyword == ptr)) && (32 == ch)))
- return PNGERR_UNSUPPORTED_FORMAT;
+ return png_error::UNSUPPORTED_FORMAT;
prev = ch;
++cnt;
ptr += len;
}
if ((32 == prev) || (1 > cnt) || (79 < cnt))
- return PNGERR_UNSUPPORTED_FORMAT;
+ return png_error::UNSUPPORTED_FORMAT;
// apply rules to text
char const *const textend(text + std::strlen(text));
@@ -863,14 +854,14 @@ png_error png_info::add_text(const char *keyword, const char *text)
char32_t ch;
int const len(uchar_from_utf8(&ch, ptr, textend - ptr));
if ((0 >= len) || (1 > ch) || (255 < ch))
- return PNGERR_UNSUPPORTED_FORMAT;
+ return png_error::UNSUPPORTED_FORMAT;
ptr += len;
}
// allocate a new text element
try { textlist.emplace_back(std::piecewise_construct, std::forward_as_tuple(keyword, kwend), std::forward_as_tuple(text, textend)); }
- catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; }
- return PNGERR_NONE;
+ catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
+ return png_error::NONE;
}
@@ -879,7 +870,7 @@ png_error png_info::add_text(const char *keyword, const char *text)
the given file
-------------------------------------------------*/
-static png_error write_chunk(util::core_file &fp, const uint8_t *data, uint32_t type, uint32_t length)
+static png_error write_chunk(core_file &fp, const uint8_t *data, uint32_t type, uint32_t length)
{
uint8_t tempbuff[8];
uint32_t crc;
@@ -891,22 +882,22 @@ static png_error write_chunk(util::core_file &fp, const uint8_t *data, uint32_t
/* write that data */
if (fp.write(tempbuff, 8) != 8)
- return PNGERR_FILE_ERROR;
+ return png_error::FILE_ERROR;
/* append the actual data */
if (length > 0)
{
if (fp.write(data, length) != length)
- return PNGERR_FILE_ERROR;
+ return png_error::FILE_ERROR;
crc = crc32(crc, data, length);
}
/* write the CRC */
put_32bit(tempbuff, crc);
if (fp.write(tempbuff, 4) != 4)
- return PNGERR_FILE_ERROR;
+ return png_error::FILE_ERROR;
- return PNGERR_NONE;
+ return png_error::NONE;
}
@@ -915,7 +906,7 @@ static png_error write_chunk(util::core_file &fp, const uint8_t *data, uint32_t
chunk to the given file by deflating it
-------------------------------------------------*/
-static png_error write_deflated_chunk(util::core_file &fp, uint8_t *data, uint32_t type, uint32_t length)
+static png_error write_deflated_chunk(core_file &fp, uint8_t *data, uint32_t type, uint32_t length)
{
uint64_t lengthpos = fp.tell();
uint8_t tempbuff[8192];
@@ -931,7 +922,7 @@ static png_error write_deflated_chunk(util::core_file &fp, uint8_t *data, uint32
/* write that data */
if (fp.write(tempbuff, 8) != 8)
- return PNGERR_FILE_ERROR;
+ return png_error::FILE_ERROR;
/* initialize the stream */
memset(&stream, 0, sizeof(stream));
@@ -939,7 +930,7 @@ static png_error write_deflated_chunk(util::core_file &fp, uint8_t *data, uint32
stream.avail_in = length;
zerr = deflateInit(&stream, Z_DEFAULT_COMPRESSION);
if (zerr != Z_OK)
- return PNGERR_COMPRESS_ERROR;
+ return png_error::COMPRESS_ERROR;
/* now loop until we run out of data */
for ( ; ; )
@@ -956,7 +947,7 @@ static png_error write_deflated_chunk(util::core_file &fp, uint8_t *data, uint32
if (fp.write(tempbuff, bytes) != bytes)
{
deflateEnd(&stream);
- return PNGERR_FILE_ERROR;
+ return png_error::FILE_ERROR;
}
crc = crc32(crc, tempbuff, bytes);
zlength += bytes;
@@ -970,29 +961,29 @@ static png_error write_deflated_chunk(util::core_file &fp, uint8_t *data, uint32
if (zerr != Z_OK)
{
deflateEnd(&stream);
- return PNGERR_COMPRESS_ERROR;
+ return png_error::COMPRESS_ERROR;
}
}
/* clean up deflater(maus) */
zerr = deflateEnd(&stream);
if (zerr != Z_OK)
- return PNGERR_COMPRESS_ERROR;
+ return png_error::COMPRESS_ERROR;
/* write the CRC */
put_32bit(tempbuff, crc);
if (fp.write(tempbuff, 4) != 4)
- return PNGERR_FILE_ERROR;
+ return png_error::FILE_ERROR;
/* seek back and update the length */
fp.seek(lengthpos, SEEK_SET);
put_32bit(tempbuff + 0, zlength);
if (fp.write(tempbuff, 4) != 4)
- return PNGERR_FILE_ERROR;
+ return png_error::FILE_ERROR;
/* return to the end */
fp.seek(lengthpos + 8 + zlength + 4, SEEK_SET);
- return PNGERR_NONE;
+ return png_error::NONE;
}
@@ -1016,7 +1007,7 @@ static png_error convert_bitmap_to_image_palette(png_info &pnginfo, const bitmap
/* allocate memory for the palette */
try { pnginfo.palette.reset(new std::uint8_t [3 * 256]); }
- catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; }
+ catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
/* build the palette */
std::fill_n(pnginfo.palette.get(), 3 * 256, 0);
@@ -1036,7 +1027,7 @@ static png_error convert_bitmap_to_image_palette(png_info &pnginfo, const bitmap
catch (std::bad_alloc const &)
{
pnginfo.palette.reset();
- return PNGERR_OUT_OF_MEMORY;
+ return png_error::OUT_OF_MEMORY;
}
/* copy in the pixels, specifying a nullptr filter */
@@ -1051,7 +1042,7 @@ static png_error convert_bitmap_to_image_palette(png_info &pnginfo, const bitmap
*dst++ = *src++;
}
- return PNGERR_NONE;
+ return png_error::NONE;
}
@@ -1075,7 +1066,7 @@ static png_error convert_bitmap_to_image_rgb(png_info &pnginfo, const bitmap_t &
/* allocate memory for the image */
try { pnginfo.image.reset(new std::uint8_t [pnginfo.height * (rowbytes + 1)]); }
- catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; }
+ catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
/* copy in the pixels, specifying a nullptr filter */
for (y = 0; y < pnginfo.height; y++)
@@ -1127,10 +1118,10 @@ static png_error convert_bitmap_to_image_rgb(png_info &pnginfo, const bitmap_t &
/* unsupported format */
else
- return PNGERR_UNSUPPORTED_FORMAT;
+ return png_error::UNSUPPORTED_FORMAT;
}
- return PNGERR_NONE;
+ return png_error::NONE;
}
@@ -1139,7 +1130,7 @@ static png_error convert_bitmap_to_image_rgb(png_info &pnginfo, const bitmap_t &
chunks to the given file
-------------------------------------------------*/
-static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette)
+static png_error write_png_stream(core_file &fp, png_info &pnginfo, const bitmap_t &bitmap, int palette_length, const rgb_t *palette)
{
uint8_t tempbuff[16];
png_error error;
@@ -1149,7 +1140,7 @@ static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const
error = convert_bitmap_to_image_palette(pnginfo, bitmap, palette_length, palette);
else
error = convert_bitmap_to_image_rgb(pnginfo, bitmap, palette_length, palette);
- if (error != PNGERR_NONE)
+ if (error != png_error::NONE)
return error;
// if we wanted to get clever and do filtering, we would do it here
@@ -1163,18 +1154,18 @@ static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const
put_8bit(tempbuff + 11, pnginfo.filter_method);
put_8bit(tempbuff + 12, pnginfo.interlace_method);
error = write_chunk(fp, tempbuff, PNG_CN_IHDR, 13);
- if (error != PNGERR_NONE)
+ if (error != png_error::NONE)
return error;
// write the PLTE chunk
if (pnginfo.num_palette > 0)
error = write_chunk(fp, pnginfo.palette.get(), PNG_CN_PLTE, pnginfo.num_palette * 3);
- if (error != PNGERR_NONE)
+ if (error != png_error::NONE)
return error;
- // write a single IDAT chunk */
+ // write a single IDAT chunk
error = write_deflated_chunk(fp, pnginfo.image.get(), PNG_CN_IDAT, pnginfo.height * (compute_rowbytes(pnginfo) + 1));
- if (error != PNGERR_NONE)
+ if (error != png_error::NONE)
return error;
// write TEXT chunks
@@ -1182,7 +1173,7 @@ static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const
for (png_info::png_text const &text : pnginfo.textlist)
{
try { textbuf.resize(text.first.length() + 1 + text.second.length()); }
- catch (std::bad_alloc const &) { return PNGERR_OUT_OF_MEMORY; }
+ catch (std::bad_alloc const &) { return png_error::OUT_OF_MEMORY; }
std::uint8_t *dst(&textbuf[0]);
// convert keyword to ISO-8859-1
@@ -1213,7 +1204,7 @@ static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const
}
error = write_chunk(fp, &textbuf[0], PNG_CN_tEXt, dst - &textbuf[0]);
- if (error != PNGERR_NONE)
+ if (error != png_error::NONE)
return error;
}
@@ -1222,7 +1213,7 @@ static png_error write_png_stream(util::core_file &fp, png_info &pnginfo, const
}
-png_error png_write_bitmap(util::core_file &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette)
+png_error png_write_bitmap(core_file &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette)
{
// use a dummy pnginfo if none passed to us
png_info pnginfo;
@@ -1231,7 +1222,7 @@ png_error png_write_bitmap(util::core_file &fp, png_info *info, bitmap_t const &
// write the PNG signature
if (fp.write(PNG_SIGNATURE, sizeof(PNG_SIGNATURE)) != sizeof(PNG_SIGNATURE))
- return PNGERR_FILE_ERROR;
+ return png_error::FILE_ERROR;
/* write the rest of the PNG data */
return write_png_stream(fp, *info, bitmap, palette_length, palette);
@@ -1246,7 +1237,7 @@ png_error png_write_bitmap(util::core_file &fp, png_info *info, bitmap_t const &
********************************************************************************/
/**
- * @fn png_error mng_capture_start(util::core_file &fp, bitmap_t &bitmap, unsigned rate)
+ * @fn png_error mng_capture_start(core_file &fp, bitmap_t &bitmap, unsigned rate)
*
* @brief Mng capture start.
*
@@ -1257,12 +1248,12 @@ png_error png_write_bitmap(util::core_file &fp, png_info *info, bitmap_t const &
* @return A png_error.
*/
-png_error mng_capture_start(util::core_file &fp, bitmap_t &bitmap, unsigned rate)
+png_error mng_capture_start(core_file &fp, bitmap_t &bitmap, unsigned rate)
{
uint8_t mhdr[28];
if (fp.write(MNG_Signature, 8) != 8)
- return PNGERR_FILE_ERROR;
+ return png_error::FILE_ERROR;
memset(mhdr, 0, 28);
put_32bit(mhdr + 0, bitmap.width());
@@ -1273,7 +1264,7 @@ png_error mng_capture_start(util::core_file &fp, bitmap_t &bitmap, unsigned rate
}
/**
- * @fn png_error mng_capture_frame(util::core_file &fp, png_info *info, bitmap_t &bitmap, int palette_length, const rgb_t *palette)
+ * @fn png_error mng_capture_frame(core_file &fp, png_info *info, bitmap_t &bitmap, int palette_length, const rgb_t *palette)
*
* @brief Mng capture frame.
*
@@ -1286,13 +1277,13 @@ png_error mng_capture_start(util::core_file &fp, bitmap_t &bitmap, unsigned rate
* @return A png_error.
*/
-png_error mng_capture_frame(util::core_file &fp, png_info &info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette)
+png_error mng_capture_frame(core_file &fp, png_info &info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette)
{
return write_png_stream(fp, info, bitmap, palette_length, palette);
}
/**
- * @fn png_error mng_capture_stop(util::core_file &fp)
+ * @fn png_error mng_capture_stop(core_file &fp)
*
* @brief Mng capture stop.
*
@@ -1301,7 +1292,9 @@ png_error mng_capture_frame(util::core_file &fp, png_info &info, bitmap_t const
* @return A png_error.
*/
-png_error mng_capture_stop(util::core_file &fp)
+png_error mng_capture_stop(core_file &fp)
{
return write_chunk(fp, nullptr, MNG_CN_MEND, 0);
}
+
+} // namespace util
diff --git a/src/lib/util/png.h b/src/lib/util/png.h
index 5f852fd8c16..784c92646e6 100644
--- a/src/lib/util/png.h
+++ b/src/lib/util/png.h
@@ -17,31 +17,33 @@
#include "corefile.h"
#include "osdcore.h"
+#include <cstdint>
#include <list>
#include <memory>
#include <string>
#include <utility>
+namespace util {
/***************************************************************************
CONSTANTS
***************************************************************************/
/* Error types */
-enum png_error
+enum class png_error
{
- PNGERR_NONE,
- PNGERR_OUT_OF_MEMORY,
- PNGERR_UNKNOWN_FILTER,
- PNGERR_FILE_ERROR,
- PNGERR_BAD_SIGNATURE,
- PNGERR_DECOMPRESS_ERROR,
- PNGERR_FILE_TRUNCATED,
- PNGERR_FILE_CORRUPT,
- PNGERR_UNKNOWN_CHUNK,
- PNGERR_COMPRESS_ERROR,
- PNGERR_UNSUPPORTED_FORMAT
+ NONE,
+ OUT_OF_MEMORY,
+ UNKNOWN_FILTER,
+ FILE_ERROR,
+ BAD_SIGNATURE,
+ DECOMPRESS_ERROR,
+ FILE_TRUNCATED,
+ FILE_CORRUPT,
+ UNKNOWN_CHUNK,
+ COMPRESS_ERROR,
+ UNSUPPORTED_FORMAT
};
@@ -57,7 +59,7 @@ public:
~png_info() { free_data(); }
- png_error read_file(util::core_file &fp);
+ png_error read_file(core_file &fp);
png_error copy_to_bitmap(bitmap_argb32 &bitmap, bool &hasalpha);
png_error expand_buffer_8bit();
@@ -66,7 +68,7 @@ public:
void free_data();
void reset() { free_data(); operator=(png_info()); }
- static png_error verify_header(util::core_file &fp);
+ static png_error verify_header(core_file &fp);
std::unique_ptr<std::uint8_t []> image;
std::uint32_t width, height;
@@ -99,12 +101,14 @@ private:
FUNCTION PROTOTYPES
***************************************************************************/
-png_error png_read_bitmap(util::core_file &fp, bitmap_argb32 &bitmap);
+png_error png_read_bitmap(core_file &fp, bitmap_argb32 &bitmap);
-png_error png_write_bitmap(util::core_file &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette);
+png_error png_write_bitmap(core_file &fp, png_info *info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette);
-png_error mng_capture_start(util::core_file &fp, bitmap_t &bitmap, unsigned rate);
-png_error mng_capture_frame(util::core_file &fp, png_info &info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette);
-png_error mng_capture_stop(util::core_file &fp);
+png_error mng_capture_start(core_file &fp, bitmap_t &bitmap, unsigned rate);
+png_error mng_capture_frame(core_file &fp, png_info &info, bitmap_t const &bitmap, int palette_length, const rgb_t *palette);
+png_error mng_capture_stop(core_file &fp);
+
+} // namespace util
#endif // MAME_LIB_UTIL_PNG_H