diff options
Diffstat (limited to 'src')
38 files changed, 1862 insertions, 1194 deletions
diff --git a/src/devices/imagedev/picture.cpp b/src/devices/imagedev/picture.cpp index e50f2c0d4c6..e6736fb10c9 100644 --- a/src/devices/imagedev/picture.cpp +++ b/src/devices/imagedev/picture.cpp @@ -10,7 +10,7 @@ #include "emu.h" #include "picture.h" -#include "png.h" +#include "rendutil.h" /*************************************************************************** TYPE DEFINITIONS @@ -44,15 +44,25 @@ void picture_image_device::device_start() image_init_result picture_image_device::call_load() { - if (png_read_bitmap(image_core_file(), m_picture) != PNGERR_NONE) - { - m_picture.reset(); - - // todo: try JPEG here. - return image_init_result::FAIL; + switch (render_detect_image(image_core_file())) + { + case RENDUTIL_IMGFORMAT_PNG: + render_load_png(m_picture, image_core_file()); + break; + + case RENDUTIL_IMGFORMAT_JPEG: + render_load_jpeg(m_picture, image_core_file()); + break; + + case RENDUTIL_IMGFORMAT_MSDIB: + render_load_msdib(m_picture, image_core_file()); + break; + + default: + m_picture.reset(); } - return image_init_result::PASS; + return m_picture.valid() ? image_init_result::PASS : image_init_result::FAIL; } void picture_image_device::call_unload() diff --git a/src/devices/video/fixfreq.cpp b/src/devices/video/fixfreq.cpp index 3810dc76a76..5de27dc0b08 100644 --- a/src/devices/video/fixfreq.cpp +++ b/src/devices/video/fixfreq.cpp @@ -14,10 +14,11 @@ ***************************************************************************/ #include "emu.h" -#include "ui/uimain.h" -#include "rendutil.h" #include "fixfreq.h" +#include "render.h" +#include "ui/uimain.h" + // for quick and dirty debugging #define VERBOSE 0 #define LOG_OUTPUT_STREAM std::cerr diff --git a/src/devices/video/vector.cpp b/src/devices/video/vector.cpp index 4e44603c136..46a21b535be 100644 --- a/src/devices/video/vector.cpp +++ b/src/devices/video/vector.cpp @@ -42,10 +42,11 @@ **************************************************************************** */ #include "emu.h" -#include "emuopts.h" -#include "rendutil.h" #include "vector.h" +#include "emuopts.h" +#include "render.h" + #define VECTOR_WIDTH_DENOM 512 diff --git a/src/emu/crsshair.cpp b/src/emu/crsshair.cpp index dd75ba119f1..027d8828e32 100644 --- a/src/emu/crsshair.cpp +++ b/src/emu/crsshair.cpp @@ -9,11 +9,15 @@ ***************************************************************************/ #include "emu.h" +#include "crsshair.h" + +#include "config.h" #include "emuopts.h" +#include "render.h" #include "rendutil.h" -#include "config.h" +#include "screen.h" + #include "xmlfile.h" -#include "crsshair.h" @@ -171,28 +175,42 @@ void render_crosshair::create_bitmap() rgb_t color = m_player < ARRAY_LENGTH(crosshair_colors) ? crosshair_colors[m_player] : rgb_t::white(); // if we have a bitmap and texture for this player, kill it - if (m_bitmap == nullptr) + if (!m_bitmap) { m_bitmap = std::make_unique<bitmap_argb32>(); m_texture = m_machine.render().texture_alloc(render_texture::hq_scale); } + else + { + m_bitmap->reset(); + } emu_file crossfile(m_machine.options().crosshair_path(), OPEN_FLAG_READ); if (!m_name.empty()) { // look for user specified file - std::string filename = m_name + ".png"; - render_load_png(*m_bitmap, crossfile, nullptr, filename.c_str()); + if (crossfile.open(m_name + ".png") == osd_file::error::NONE) + { + render_load_png(*m_bitmap, crossfile); + crossfile.close(); + } } else { // look for default cross?.png in crsshair/game dir - std::string filename = string_format("cross%d.png", m_player + 1); - render_load_png(*m_bitmap, crossfile, m_machine.system().name, filename.c_str()); + std::string const filename = string_format("cross%d.png", m_player + 1); + if (crossfile.open(m_machine.system().name + (PATH_SEPARATOR + filename)) == osd_file::error::NONE) + { + render_load_png(*m_bitmap, crossfile); + crossfile.close(); + } // look for default cross?.png in crsshair dir - if (!m_bitmap->valid()) - render_load_png(*m_bitmap, crossfile, nullptr, filename.c_str()); + if (!m_bitmap->valid() && (crossfile.open(filename) == osd_file::error::NONE)) + { + render_load_png(*m_bitmap, crossfile); + crossfile.close(); + } } /* if that didn't work, use the built-in one */ diff --git a/src/emu/recording.cpp b/src/emu/recording.cpp index 2e134f03f4a..dd75c27d62f 100644 --- a/src/emu/recording.cpp +++ b/src/emu/recording.cpp @@ -248,7 +248,7 @@ mng_movie_recording::mng_movie_recording(screen_device *screen, std::map<std::st mng_movie_recording::~mng_movie_recording() { if (m_mng_file) - mng_capture_stop(*m_mng_file); + util::mng_capture_stop(*m_mng_file); } @@ -264,10 +264,10 @@ bool mng_movie_recording::initialize(std::unique_ptr<emu_file> &&file, bitmap_t set_frame_period(attotime::from_hz(rate)); m_mng_file = std::move(file); - png_error pngerr = mng_capture_start(*m_mng_file, snap_bitmap, rate); - if (pngerr != PNGERR_NONE) - osd_printf_error("Error capturing MNG, png_error=%d\n", pngerr); - return pngerr == PNGERR_NONE; + util::png_error pngerr = util::mng_capture_start(*m_mng_file, snap_bitmap, rate); + if (pngerr != util::png_error::NONE) + osd_printf_error("Error capturing MNG, png_error=%d\n", std::underlying_type_t<util::png_error>(pngerr)); + return pngerr == util::png_error::NONE; } @@ -278,15 +278,15 @@ bool mng_movie_recording::initialize(std::unique_ptr<emu_file> &&file, bitmap_t bool mng_movie_recording::append_single_video_frame(bitmap_rgb32 &bitmap, const rgb_t *palette, int palette_entries) { // set up the text fields in the movie info - png_info pnginfo; + util::png_info pnginfo; if (current_frame() == 0) { for (auto &ent : m_info_fields) pnginfo.add_text(ent.first.c_str(), ent.second.c_str()); } - png_error error = mng_capture_frame(*m_mng_file, pnginfo, bitmap, palette_entries, palette); - return error == png_error::PNGERR_NONE; + util::png_error error = util::mng_capture_frame(*m_mng_file, pnginfo, bitmap, palette_entries, palette); + return error == util::png_error::NONE; } diff --git a/src/emu/render.cpp b/src/emu/render.cpp index 531e327c978..b1518d87dd4 100644 --- a/src/emu/render.cpp +++ b/src/emu/render.cpp @@ -1371,9 +1371,9 @@ render_primitive_list &render_target::get_primitives() { // if we are not in the running stage, draw an outer box render_primitive *prim = list.alloc(render_primitive::QUAD); - set_render_bounds_xy(prim->bounds, 0.0f, 0.0f, (float)m_width, (float)m_height); + prim->bounds.set_xy(0.0f, 0.0f, (float)m_width, (float)m_height); prim->full_bounds = prim->bounds; - set_render_color(&prim->color, 1.0f, 0.1f, 0.1f, 0.1f); + prim->color.set(1.0f, 0.1f, 0.1f, 0.1f); prim->texture.base = nullptr; prim->flags = PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA); list.append(*prim); @@ -1381,9 +1381,9 @@ render_primitive_list &render_target::get_primitives() if (m_width > 1 && m_height > 1) { prim = list.alloc(render_primitive::QUAD); - set_render_bounds_xy(prim->bounds, 1.0f, 1.0f, (float)(m_width - 1), (float)(m_height - 1)); + prim->bounds.set_xy(1.0f, 1.0f, float(m_width - 1), float(m_height - 1)); prim->full_bounds = prim->bounds; - set_render_color(&prim->color, 1.0f, 0.0f, 0.0f, 0.0f); + prim->color.set(1.0f, 0.0f, 0.0f, 0.0f); prim->texture.base = nullptr; prim->flags = PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA); list.append(*prim); @@ -1535,7 +1535,7 @@ bool render_target::map_point_input(s32 target_x, s32 target_y, ioport_port *&in if (item.has_input()) { // point successfully mapped - input_port = item.input_tag_and_mask(input_mask); + std::tie(input_port, input_mask) = item.input_tag_and_mask(); input_x = (target_f.first - bounds.x0) / bounds.width(); input_y = (target_f.second - bounds.y0) / bounds.height(); return true; @@ -2237,7 +2237,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const cliprect.y0 = xform.yoffs; cliprect.x1 = xform.xoffs + xform.xscale; cliprect.y1 = xform.yoffs + xform.yscale; - sect_render_bounds(cliprect, m_bounds); + cliprect &= m_bounds; float root_xoffs = root_xform.xoffs + fabsf(root_xform.xscale - xform.xscale) * 0.5f; float root_yoffs = root_xform.yoffs + fabsf(root_xform.yscale - xform.yscale) * 0.5f; @@ -2247,7 +2247,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const root_cliprect.y0 = root_yoffs; root_cliprect.x1 = root_xoffs + root_xform.xscale; root_cliprect.y1 = root_yoffs + root_xform.yscale; - sect_render_bounds(root_cliprect, m_bounds); + root_cliprect &= m_bounds; // compute the container transform object_transform container_xform; @@ -2477,7 +2477,7 @@ void render_target::add_container_primitives(render_primitive_list &list, const // allocate a primitive render_primitive *prim = list.alloc(render_primitive::QUAD); - set_render_bounds_wh(prim->bounds, xform.xoffs, xform.yoffs, xform.xscale, xform.yscale); + prim->bounds.set_wh(xform.xoffs, xform.yoffs, xform.xscale, xform.yscale); prim->full_bounds = prim->bounds; prim->color = container_xform.color; width = render_round_nearest(prim->bounds.x1) - render_round_nearest(prim->bounds.x0); @@ -2525,7 +2525,7 @@ void render_target::add_element_primitives(render_primitive_list &list, const ob // compute the bounds s32 width = render_round_nearest(xform.xscale); s32 height = render_round_nearest(xform.yscale); - set_render_bounds_wh(prim->bounds, render_round_nearest(xform.xoffs), render_round_nearest(xform.yoffs), (float) width, (float) height); + prim->bounds.set_wh(render_round_nearest(xform.xoffs), render_round_nearest(xform.yoffs), float(width), float(height)); prim->full_bounds = prim->bounds; if (xform.orientation & ORIENTATION_SWAP_XY) std::swap(width, height); @@ -2542,7 +2542,7 @@ void render_target::add_element_primitives(render_primitive_list &list, const ob cliprect.y0 = render_round_nearest(xform.yoffs); cliprect.x1 = render_round_nearest(xform.xoffs + xform.xscale); cliprect.y1 = render_round_nearest(xform.yoffs + xform.yscale); - sect_render_bounds(cliprect, m_bounds); + cliprect &= m_bounds; // determine UV coordinates and apply clipping prim->texcoords = oriented_texcoords[xform.orientation]; @@ -2956,9 +2956,9 @@ void render_target::add_clear_extents(render_primitive_list &list) if (x1 - x0 > 0) { render_primitive *prim = list.alloc(render_primitive::QUAD); - set_render_bounds_xy(prim->bounds, (float)x0, (float)y0, (float)x1, (float)y1); + prim->bounds.set_xy(float(x0), float(y0), float(x1), float(y1)); prim->full_bounds = prim->bounds; - set_render_color(&prim->color, 1.0f, 0.0f, 0.0f, 0.0f); + prim->color.set(1.0f, 0.0f, 0.0f, 0.0f); prim->texture.base = nullptr; prim->flags = PRIMFLAG_BLENDMODE(BLENDMODE_ALPHA); clearlist.append(*prim); @@ -3012,7 +3012,7 @@ void render_target::add_clear_and_optimize_primitive_list(render_primitive_list if (PRIMFLAG_GET_BLENDMODE(prim.flags) == BLENDMODE_RGB_MULTIPLY) { // RGB multiply will multiply against 0, leaving nothing - set_render_color(&prim.color, 1.0f, 0.0f, 0.0f, 0.0f); + prim.color.set(1.0f, 0.0f, 0.0f, 0.0f); prim.texture.base = nullptr; prim.flags = (prim.flags & ~PRIMFLAG_BLENDMODE_MASK) | PRIMFLAG_BLENDMODE(BLENDMODE_NONE); } diff --git a/src/emu/render.h b/src/emu/render.h index 6a76e23eda4..c78fa8fb7d2 100644 --- a/src/emu/render.h +++ b/src/emu/render.h @@ -46,6 +46,7 @@ #ifndef MAME_EMU_RENDER_H #define MAME_EMU_RENDER_H +#include "rendertypes.h" #include "screen.h" #include <array> @@ -65,17 +66,6 @@ // CONSTANTS //************************************************************************** -// blending modes -enum -{ - BLENDMODE_NONE = 0, // no blending - BLENDMODE_ALPHA, // standard alpha blend - BLENDMODE_RGB_MULTIPLY, // apply source alpha to source pix, then multiply RGB values - BLENDMODE_ADD, // apply source alpha to source pix, then add to destination - - BLENDMODE_COUNT -}; - // render creation flags constexpr u8 RENDER_CREATE_NO_ART = 0x01; // ignore any views that have art in them @@ -166,62 +156,6 @@ constexpr u32 PRIMFLAG_GET_VECTORBUF(u32 x) { return (x & PRIMFLAG_VECTORBUF_MAS // texture scaling callback typedef void (*texture_scaler_func)(bitmap_argb32 &dest, bitmap_argb32 &source, const rectangle &sbounds, void *param); -// render_bounds - floating point bounding rectangle -struct render_bounds -{ - float x0; // leftmost X coordinate - float y0; // topmost Y coordinate - float x1; // rightmost X coordinate - float y1; // bottommost Y coordinate - - constexpr float width() const { return x1 - x0; } - constexpr float height() const { return y1 - y0; } - constexpr float aspect() const { return width() / height(); } - constexpr bool includes(float x, float y) const { return (x >= x0) && (x <= x1) && (y >= y0) && (y <= y1); } -}; - - -// render_color - floating point set of ARGB values -struct render_color -{ - float a; // alpha component (0.0 = transparent, 1.0 = opaque) - float r; // red component (0.0 = none, 1.0 = max) - float g; // green component (0.0 = none, 1.0 = max) - float b; // blue component (0.0 = none, 1.0 = max) - - constexpr render_color operator*(render_color const &c) const - { - return render_color{ a * c.a, r * c.r, g * c.g, b * c.b }; - } - - render_color &operator*=(render_color const &c) - { - a *= c.a; - r *= c.r; - g *= c.g; - b *= c.b; - return *this; - } -}; - - -// render_texuv - floating point set of UV texture coordinates -struct render_texuv -{ - float u; // U coordinate (0.0-1.0) - float v; // V coordinate (0.0-1.0) -}; - - -// render_quad_texuv - floating point set of UV texture coordinates -struct render_quad_texuv -{ - render_texuv tl; // top-left UV coordinate - render_texuv tr; // top-right UV coordinate - render_texuv bl; // bottom-left UV coordinate - render_texuv br; // bottom-right UV coordinate -}; - // render_texinfo - texture information struct render_texinfo @@ -818,7 +752,7 @@ public: // interactivity bool has_input() const { return bool(m_input_port); } - ioport_port *input_tag_and_mask(ioport_value &mask) const { mask = m_input_mask; return m_input_port; }; + std::pair<ioport_port *, ioport_value> input_tag_and_mask() const { return std::make_pair(m_input_port, m_input_mask); }; bool clickthrough() const { return m_clickthrough; } // fetch state based on configured source @@ -831,13 +765,17 @@ public: using bounds_vector = emu::render::detail::bounds_vector; using color_vector = emu::render::detail::color_vector; + int animation_state() const; + static layout_element *find_element(view_environment &env, util::xml::data_node const &itemnode, element_map &elemmap); static bounds_vector make_bounds(view_environment &env, util::xml::data_node const &itemnode, layout_group::transform const &trans); static color_vector make_color(view_environment &env, util::xml::data_node const &itemnode, render_color const &mult); static std::string make_animoutput_tag(view_environment &env, util::xml::data_node const &itemnode); + static std::string make_animinput_tag(view_environment &env, util::xml::data_node const &itemnode); + static ioport_value make_animmask(view_environment &env, util::xml::data_node const &itemnode); static std::string make_input_tag(view_environment &env, util::xml::data_node const &itemnode); static int get_blend_mode(view_environment &env, util::xml::data_node const &itemnode); - static unsigned get_input_shift(ioport_value mask); + static unsigned get_state_shift(ioport_value mask); // internal state layout_element *const m_element; // pointer to the associated element (non-screens only) @@ -845,6 +783,9 @@ public: output_finder<> m_animoutput; // associated output for animation if different bool const m_have_output; // whether we actually have an output bool const m_have_animoutput; // whether we actually have an output for animation + ioport_port * m_animinput_port; // input port used for animation + ioport_value const m_animmask; // mask for animation state + u8 const m_animshift; // shift for animation state ioport_port * m_input_port; // input port of this item ioport_field const * m_input_field; // input port field of this item ioport_value const m_input_mask; // input mask of this item @@ -860,6 +801,7 @@ public: // cold items std::string const m_input_tag; // input tag of this item + std::string const m_animinput_tag; // tag of input port for animation state bounds_vector const m_rawbounds; // raw (original) bounds of the item bool const m_has_clickthrough; // whether clickthrough was explicitly configured }; diff --git a/src/emu/rendertypes.h b/src/emu/rendertypes.h new file mode 100644 index 00000000000..0fa698cc931 --- /dev/null +++ b/src/emu/rendertypes.h @@ -0,0 +1,153 @@ +// license:BSD-3-Clause +// copyright-holders:Aaron Giles, Vas Crabb +/*************************************************************************** + + render.h + + Core renderer constants and structures for MAME. + +***************************************************************************/ +#ifndef MAME_EMU_RENDERTYPES_H +#define MAME_EMU_RENDERTYPES_H + +#pragma once + +#include <algorithm> + + +//************************************************************************** +// CONSTANTS +//************************************************************************** + + +// blending modes +enum +{ + BLENDMODE_NONE = 0, // no blending + BLENDMODE_ALPHA, // standard alpha blend + BLENDMODE_RGB_MULTIPLY, // apply source alpha to source pix, then multiply RGB values + BLENDMODE_ADD, // apply source alpha to source pix, then add to destination + + BLENDMODE_COUNT +}; + + + +//************************************************************************** +// TYPE DEFINITIONS +//************************************************************************** + +// render_bounds - floating point bounding rectangle +struct render_bounds +{ + float x0; // leftmost X coordinate + float y0; // topmost Y coordinate + float x1; // rightmost X coordinate + float y1; // bottommost Y coordinate + + constexpr float width() const { return x1 - x0; } + constexpr float height() const { return y1 - y0; } + constexpr float aspect() const { return width() / height(); } + constexpr bool includes(float x, float y) const { return (x >= x0) && (x <= x1) && (y >= y0) && (y <= y1); } + + // intersection + constexpr render_bounds operator&(render_bounds const &b) const + { + return render_bounds{ (std::max)(x0, b.x0), (std::max)(y0, b.y0), (std::min)(x1, b.x1), (std::min)(y1, b.y1) }; + } + + render_bounds &operator&=(render_bounds const &b) + { + x0 = (std::max)(x0, b.x0); + y0 = (std::max)(y0, b.y0); + x1 = (std::min)(x1, b.x1); + y1 = (std::min)(y1, b.y1); + return *this; + } + + // union + constexpr render_bounds operator|(render_bounds const &b) const + { + return render_bounds{ (std::min)(x0, b.x0), (std::min)(y0, b.y0), (std::max)(x1, b.x1), (std::max)(y1, b.y1) }; + } + + render_bounds &operator|=(render_bounds const &b) + { + x0 = (std::min)(x0, b.x0); + y0 = (std::min)(y0, b.y0); + x1 = (std::max)(x1, b.x1); + y1 = (std::max)(y1, b.y1); + return *this; + } + + render_bounds &set_xy(float left, float top, float right, float bottom) + { + x0 = left; + y0 = top; + x1 = right; + y1 = bottom; + return *this; + } + + render_bounds &set_wh(float left, float top, float width, float height) + { + x0 = left; + y0 = top; + x1 = left + width; + y1 = top + height; + return *this; + } +}; + + +// render_color - floating point set of ARGB values +struct render_color +{ + float a; // alpha component (0.0 = transparent, 1.0 = opaque) + float r; // red component (0.0 = none, 1.0 = max) + float g; // green component (0.0 = none, 1.0 = max) + float b; // blue component (0.0 = none, 1.0 = max) + + constexpr render_color operator*(render_color const &c) const + { + return render_color{ a * c.a, r * c.r, g * c.g, b * c.b }; + } + + render_color &operator*=(render_color const &c) + { + a *= c.a; + r *= c.r; + g *= c.g; + b *= c.b; + return *this; + } + + render_color &set(float alpha, float red, float green, float blue) + { + a = alpha; + r = red; + g = green; + b = blue; + return *this; + } +}; + + +// render_texuv - floating point set of UV texture coordinates +struct render_texuv +{ + float u; // U coordinate (0.0-1.0) + float v; // V coordinate (0.0-1.0) +}; + + +// render_quad_texuv - floating point set of UV texture coordinates +struct render_quad_texuv +{ + render_texuv tl; // top-left UV coordinate + render_texuv tr; // top-right UV coordinate + render_texuv bl; // bottom-left UV coordinate + render_texuv br; // bottom-right UV coordinate +}; + +#endif // MAME_EMU_RENDERTYPES_H diff --git a/src/emu/rendlay.cpp b/src/emu/rendlay.cpp index 940923ae0e1..2f57c7ade1d 100644 --- a/src/emu/rendlay.cpp +++ b/src/emu/rendlay.cpp @@ -17,6 +17,7 @@ #include "rendutil.h" #include "video/rgbutil.h" +#include "nanosvg.h" #include "vecstream.h" #include "xmlfile.h" @@ -27,6 +28,7 @@ #include <cstdio> #include <cstring> #include <iomanip> +#include <limits> #include <locale> #include <sstream> #include <stdexcept> @@ -578,25 +580,29 @@ private: entry_vector m_entries; util::ovectorstream m_buffer; + std::shared_ptr<NSVGrasterizer> const m_svg_rasterizer; device_t &m_device; layout_environment *const m_next = nullptr; bool m_cached = false; public: explicit layout_environment(device_t &device) - : m_device(device) + : m_svg_rasterizer(nsvgCreateRasterizer(), util::nsvg_deleter()) + , m_device(device) { } explicit layout_environment(layout_environment &next) - : m_device(next.m_device) + : m_svg_rasterizer(next.m_svg_rasterizer) + , m_device(next.m_device) , m_next(&next) { } layout_environment(layout_environment const &) = delete; - device_t &device() { return m_device; } - running_machine &machine() { return device().machine(); } - bool is_root_device() { return &device() == &machine().root_device(); } + device_t &device() const { return m_device; } + running_machine &machine() const { return device().machine(); } + bool is_root_device() const { return &device() == &machine().root_device(); } + std::shared_ptr<NSVGrasterizer> const &svg_rasterizer() const { return m_svg_rasterizer; } void set_parameter(std::string &&name, std::string &&value) { @@ -785,34 +791,44 @@ public: void parse_bounds(util::xml::data_node const *node, render_bounds &result) { - // default to unit rectangle if (!node) { + // default to unit rectangle result.x0 = result.y0 = 0.0F; result.x1 = result.y1 = 1.0F; } else { - // parse attributes + // horizontal position/size if (node->has_attribute("left")) { - // left/right/top/bottom format result.x0 = get_attribute_float(*node, "left", 0.0F); result.x1 = get_attribute_float(*node, "right", 1.0F); - result.y0 = get_attribute_float(*node, "top", 0.0F); - result.y1 = get_attribute_float(*node, "bottom", 1.0F); } - else if (node->has_attribute("x")) + else { - // x/y/width/height format - result.x0 = get_attribute_float(*node, "x", 0.0F); - result.x1 = result.x0 + get_attribute_float(*node, "width", 1.0F); - result.y0 = get_attribute_float(*node, "y", 0.0F); - result.y1 = result.y0 + get_attribute_float(*node, "height", 1.0F); + float const width = get_attribute_float(*node, "width", 1.0F); + if (node->has_attribute("xc")) + result.x0 = get_attribute_float(*node, "xc", 0.0F) - (width / 2.0F); + else + result.x0 = get_attribute_float(*node, "x", 0.0F); + result.x1 = result.x0 + width; + } + + // vertical position/size + if (node->has_attribute("top")) + { + result.y0 = get_attribute_float(*node, "top", 0.0F); + result.y1 = get_attribute_float(*node, "bottom", 1.0F); } else { - throw layout_syntax_error("bounds element requires either left or x attribute"); + float const height = get_attribute_float(*node, "height", 1.0F); + if (node->has_attribute("yc")) + result.y0 = get_attribute_float(*node, "yc", 0.0F) - (height / 2.0F); + else + result.y0 = get_attribute_float(*node, "y", 0.0F); + result.y1 = result.y0 + height; } // check for errors @@ -978,7 +994,7 @@ render_bounds accumulate_bounds(emu::render::detail::bounds_vector const &steps) auto i(steps.begin()); render_bounds result(i->bounds); while (steps.end() != ++i) - union_render_bounds(result, i->bounds); + result |= i->bounds; return result; } @@ -1123,7 +1139,7 @@ layout_element::layout_element(environment &env, util::xml::data_node const &ele if (first) bounds = newcomp.overall_bounds(); else - union_render_bounds(bounds, newcomp.overall_bounds()); + bounds |= newcomp.overall_bounds(); first = false; // determine the maximum state @@ -1286,7 +1302,7 @@ void layout_group::resolve_bounds(environment &env, group_map &groupmap, std::ve seen.push_back(this); if (!m_bounds_resolved) { - set_render_bounds_xy(m_bounds, 0.0F, 0.0F, 1.0F, 1.0F); + m_bounds.set_xy(0.0F, 0.0F, 1.0F, 1.0F); environment local(env); bool empty(true); resolve_bounds(local, m_groupnode, groupmap, seen, empty, false, false, true); @@ -1349,13 +1365,13 @@ void layout_group::resolve_bounds( { render_bounds b; env.parse_bounds(boundsnode, b); - union_render_bounds(itembounds, b); + itembounds |= b; } } if (empty) m_bounds = itembounds; else - union_render_bounds(m_bounds, itembounds); + m_bounds |= itembounds; empty = false; LOGMASKED(LOG_GROUP_BOUNDS_RESOLUTION, "Accumulate item bounds (%s %s %s %s) -> (%s %s %s %s)\n", itembounds.x0, itembounds.y0, itembounds.x1, itembounds.y1, @@ -1371,7 +1387,7 @@ void layout_group::resolve_bounds( if (empty) m_bounds = itembounds; else - union_render_bounds(m_bounds, itembounds); + m_bounds |= itembounds; empty = false; LOGMASKED(LOG_GROUP_BOUNDS_RESOLUTION, "Accumulate group '%s' reference explicit bounds (%s %s %s %s) -> (%s %s %s %s)\n", itemnode->get_attribute_string("ref", ""), @@ -1399,7 +1415,7 @@ void layout_group::resolve_bounds( if (empty) m_bounds = itembounds; else - union_render_bounds(m_bounds, itembounds); + m_bounds |= itembounds; empty = false; unresolved = false; LOGMASKED(LOG_GROUP_BOUNDS_RESOLUTION, "Accumulate group '%s' reference computed bounds (%s %s %s %s) -> (%s %s %s %s)\n", @@ -1523,11 +1539,13 @@ class layout_element::image_component : public component { public: // construction/destruction - image_component(environment &env, util::xml::data_node const &compnode, const char *dirname) + image_component(environment &env, util::xml::data_node const &compnode, char const *dirname) : component(env, compnode, dirname) + , m_rasterizer(env.svg_rasterizer()) , m_dirname(dirname ? dirname : "") , m_imagefile(env.get_attribute_string(compnode, "file", "")) , m_alphafile(env.get_attribute_string(compnode, "alphafile", "")) + , m_data(get_data(compnode)) { } @@ -1535,97 +1553,204 @@ public: virtual void preload(running_machine &machine) override { if (!m_bitmap.valid()) - load_bitmap(machine); + load_image(machine); } - virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override + virtual void draw(running_machine &machine, bitmap_argb32 &dest, rectangle const &bounds, int state) override { - if (!m_bitmap.valid()) - load_bitmap(machine); + if (!m_bitmap.valid() && !m_svg) + load_image(machine); + + if (m_bitmap.valid()) + draw_bitmap(dest, bounds, state); + else if (m_svg) + draw_svg(dest, bounds, state); + } +private: + // internal helpers + void draw_bitmap(bitmap_argb32 &dest, rectangle const &bounds, int state) + { render_color const c(color(state)); if (m_hasalpha || (1.0F > c.a)) { bitmap_argb32 tempbitmap(dest.width(), dest.height()); render_resample_argb_bitmap_hq(tempbitmap, m_bitmap, c); - for (s32 y0 = 0, y1 = bounds.top(); bounds.bottom() >= y1; ++y0, ++y1) + alpha_blend(tempbitmap, dest, bounds); + } + else + { + bitmap_argb32 destsub(dest, bounds); + render_resample_argb_bitmap_hq(destsub, m_bitmap, c); + } + } + + void draw_svg(bitmap_argb32 &dest, rectangle const &bounds, int state) + { + // rasterise into a temporary bitmap + float const xscale(bounds.width() / m_svg->width); + float const yscale(bounds.height() / m_svg->height); + float const drawscale((std::max)(xscale, yscale)); + bitmap_argb32 tempbitmap(int(m_svg->width * drawscale), int(m_svg->height * drawscale)); + nsvgRasterize( + m_rasterizer.get(), + m_svg.get(), + 0, 0, drawscale, + reinterpret_cast<unsigned char *>(&tempbitmap.pix(0)), + tempbitmap.width(), tempbitmap.height(), + tempbitmap.rowbytes()); + + // correct colour format and multiply by state colour + bool havealpha(false); + render_color const c(color(state)); + for (s32 y = 0; tempbitmap.height() > y; ++y) + { + u32 *dst(&tempbitmap.pix(y)); + for (s32 x = 0; tempbitmap.width() > x; ++x, ++dst) + { + u8 const *const src(reinterpret_cast<u8 const *>(dst)); + rgb_t const d( + u8((float(src[3]) * c.a) + 0.5), + u8((float(src[0]) * c.r) + 0.5), + u8((float(src[1]) * c.g) + 0.5), + u8((float(src[2]) * c.b) + 0.5)); + *dst = d; + havealpha = havealpha || (d.a() < 255U); + } + } + + // find most efficient way to insert it in the target bitmap + if (!havealpha) + { + if ((tempbitmap.width() == bounds.width()) && (tempbitmap.height() == bounds.height())) + { + for (s32 y = 0; tempbitmap.height() > y; ++y) + std::copy_n(&tempbitmap.pix(y), bounds.width(), &dest.pix(y + bounds.top(), bounds.left())); + } + else { - u32 const *src(&tempbitmap.pix(y0, 0)); - u32 *dst(&dest.pix(y1, bounds.left())); - for (s32 x1 = bounds.left(); bounds.right() >= x1; ++x1, ++src, ++dst) + bitmap_argb32 destsub(dest, bounds); + render_resample_argb_bitmap_hq(destsub, tempbitmap, render_color{ 1.0F, 1.0F, 1.0F, 1.0F }); + } + } + else if ((tempbitmap.width() == bounds.width()) && (tempbitmap.height() == bounds.height())) + { + alpha_blend(tempbitmap, dest, bounds); + } + else + { + bitmap_argb32 scaled(bounds.width(), bounds.height()); + render_resample_argb_bitmap_hq(scaled, tempbitmap, render_color{ 1.0F, 1.0F, 1.0F, 1.0F }); + tempbitmap.reset(); + alpha_blend(scaled, dest, bounds); + } + } + + void alpha_blend(bitmap_argb32 const &srcbitmap, bitmap_argb32 &dstbitmap, rectangle const &bounds) + { + for (s32 y0 = 0, y1 = bounds.top(); bounds.bottom() >= y1; ++y0, ++y1) + { + u32 const *src(&srcbitmap.pix(y0, 0)); + u32 *dst(&dstbitmap.pix(y1, bounds.left())); + for (s32 x1 = bounds.left(); bounds.right() >= x1; ++x1, ++src, ++dst) + { + rgb_t const a(*src); + u32 const aa(a.a()); + if (255 == aa) + { + *dst = *src; + } + else if (aa) { - rgb_t const a(*src); - u32 const aa(a.a()); - if (255 == aa) + rgb_t const b(*dst); + u32 const ba(b.a()); + if (ba) { - *dst = *src; + u32 const ca((aa * 255) + (ba * (255 - aa))); + *dst = rgb_t( + u8(ca / 255), + u8(((a.r() * aa * 255) + (b.r() * ba * (255 - aa))) / ca), + u8(((a.g() * aa * 255) + (b.g() * ba * (255 - aa))) / ca), + u8(((a.b() * aa * 255) + (b.b() * ba * (255 - aa))) / ca)); } - else if (aa) + else { - rgb_t const b(*dst); - u32 const ba(b.a()); - if (ba) - { - u32 const ca((aa * 255) + (ba * (255 - aa))); - *dst = rgb_t( - u8(ca / 255), - u8(((a.r() * aa * 255) + (b.r() * ba * (255 - aa))) / ca), - u8(((a.g() * aa * 255) + (b.g() * ba * (255 - aa))) / ca), - u8(((a.b() * aa * 255) + (b.b() * ba * (255 - aa))) / ca)); - } - else - { - *dst = *src; - } + *dst = *src; } } } } - else - { - bitmap_argb32 destsub(dest, bounds); - render_resample_argb_bitmap_hq(destsub, m_bitmap, c); - } } -private: - // internal helpers - void load_bitmap(running_machine &machine) + void load_image(running_machine &machine) { - LOGMASKED(LOG_IMAGE_LOAD, "Image component attempt to load image file '%s/%s'\n", m_dirname, m_imagefile); + // attempt to open the file + LOGMASKED(LOG_IMAGE_LOAD, "Image component attempt to load image file '%s%s%s'\n", m_dirname, PATH_SEPARATOR, m_imagefile); emu_file file(machine.options().art_path(), OPEN_FLAG_READ); - - ru_imgformat const format = render_detect_image(file, m_dirname.c_str(), m_imagefile.c_str()); - switch (format) + osd_file::error const imgerr = file.open(m_dirname.empty() ? m_imagefile : (m_dirname + PATH_SEPARATOR + m_imagefile)); + if (osd_file::error::NONE == imgerr) { - case RENDUTIL_IMGFORMAT_ERROR: - LOGMASKED(LOG_IMAGE_LOAD, "Image component error detecting image file format\n"); - break; - - case RENDUTIL_IMGFORMAT_PNG: - // load the basic bitmap - LOGMASKED(LOG_IMAGE_LOAD, "Image component detected PNG file format\n"); - m_hasalpha = render_load_png(m_bitmap, file, m_dirname.c_str(), m_imagefile.c_str()); - break; - - default: - // try JPG - LOGMASKED(LOG_IMAGE_LOAD, "Image component failed to detect image file format, trying JPEG\n"); - render_load_jpeg(m_bitmap, file, m_dirname.c_str(), m_imagefile.c_str()); - break; + ru_imgformat const format = render_detect_image(file); + switch (format) + { + case RENDUTIL_IMGFORMAT_ERROR: + LOGMASKED(LOG_IMAGE_LOAD, "Image component error detecting image file format\n"); + break; + + case RENDUTIL_IMGFORMAT_PNG: + LOGMASKED(LOG_IMAGE_LOAD, "Image component detected PNG file format\n"); + m_hasalpha = render_load_png(m_bitmap, file); + break; + + case RENDUTIL_IMGFORMAT_JPEG: + LOGMASKED(LOG_IMAGE_LOAD, "Image component detected JPEG file format\n"); + render_load_jpeg(m_bitmap, file); + break; + + case RENDUTIL_IMGFORMAT_MSDIB: + LOGMASKED(LOG_IMAGE_LOAD, "Image component detected Microsoft DIB file format\n"); + render_load_msdib(m_bitmap, file); + break; + + default: + LOGMASKED(LOG_IMAGE_LOAD, "Image component failed to detect image file format, trying SVG\n"); + load_svg(file); + break; + } + file.close(); + } + else + { + LOGMASKED(LOG_IMAGE_LOAD, "Image component unable to open image file '%s%s%s'\n", m_dirname, PATH_SEPARATOR, m_imagefile); } // load the alpha bitmap if specified - if (m_bitmap.valid() && !m_alphafile.empty()) + if (!m_alphafile.empty()) { - // TODO: no way to detect corner case where we had alpha from the image but the alpha PNG makes it entirely opaque - LOGMASKED(LOG_IMAGE_LOAD, "Image component attempt to load alpha channel from file '%s/%s'\n", m_dirname, m_alphafile); - if (render_load_png(m_bitmap, file, m_dirname.c_str(), m_alphafile.c_str(), true)) - m_hasalpha = true; + if (m_bitmap.valid()) + { + LOGMASKED(LOG_IMAGE_LOAD, "Image component attempt to load alpha channel from file '%s%s%s'\n", m_dirname, PATH_SEPARATOR, m_alphafile); + osd_file::error const alferr = file.open(m_dirname.empty() ? m_alphafile : (m_dirname + PATH_SEPARATOR + m_alphafile)); + if (osd_file::error::NONE == alferr) + { + // TODO: no way to detect corner case where we had alpha from the image but the alpha PNG makes it entirely opaque + if (render_load_png(m_bitmap, file, true)) + m_hasalpha = true; + file.close(); + } + else + { + LOGMASKED(LOG_IMAGE_LOAD, "Image component unable to open alpha channel file '%s%s%s'\n", m_dirname, PATH_SEPARATOR, m_alphafile); + } + } + else if (m_svg) + { + osd_printf_warning("Component alpha channel file '%s' ignored for SVG image '%s'\n", m_alphafile, m_imagefile); + } } - // if we can't load the bitmap, allocate a dummy one and report an error - if (!m_bitmap.valid()) + // if we can't load an image, allocate a dummy one and report an error + if (!m_bitmap.valid() && !m_svg) { // draw some stripes in the bitmap m_bitmap.allocate(100, 100); @@ -1636,18 +1761,87 @@ private: // log an error if (m_alphafile.empty()) - osd_printf_warning("Unable to load component bitmap '%s'\n", m_imagefile); + osd_printf_warning("Unable to load component image '%s'\n", m_imagefile); else - osd_printf_warning("Unable to load component bitmap '%s'/'%s'\n", m_imagefile, m_alphafile); + osd_printf_warning("Unable to load component image '%s'/'%s'\n", m_imagefile, m_alphafile); + } + + // clear out this stuff in case it's large + if (!m_svg) + m_rasterizer.reset(); + m_dirname.clear(); + m_imagefile.clear(); + m_alphafile.clear(); + m_data.clear(); + } + + void load_svg(util::core_file &file) + { + if (!m_rasterizer) + { + osd_printf_warning("No SVG rasteriser available, won't attempt to parse component image '%s' as SVG\n", m_imagefile); + return; + } + u64 len(file.size()); + if ((std::numeric_limits<size_t>::max() - 1) < len) + { + osd_printf_warning("Component image '%s' is too large to read into memory\n", m_imagefile); + return; + } + std::unique_ptr<char []> svgbuf(new (std::nothrow) char [size_t(len) + 1]); + if (!svgbuf) + { + osd_printf_warning("Error allocating memory to read component image '%s'\n", m_imagefile); + return; + } + svgbuf[len] = '\0'; + for (char *ptr = svgbuf.get(); len; ) + { + u32 const block(u32(std::min<u64>(std::numeric_limits<u32>::max(), len))); + u32 const read(file.read(ptr, block)); + if (!read) + { + osd_printf_warning("Error reading component image '%s'\n", m_imagefile); + return; + } + ptr += read; + len -= read; + } + m_svg.reset(nsvgParse(svgbuf.get(), "px", 72)); + svgbuf.reset(); + if (!m_svg) + { + osd_printf_warning("Failed to parse component image '%s' as SVG\n", m_imagefile); + return; + } + if ((0.0F >= m_svg->width) || (0.0F >= m_svg->height)) + { + osd_printf_warning("Parsing component image '%s' as SVG produced empty image\n", m_imagefile); + m_svg.reset(); + return; } } + static std::string get_data(util::xml::data_node const &compnode) + { + util::xml::data_node const *datanode(compnode.get_child("data")); + if (datanode && datanode->get_value()) + return datanode->get_value(); + else + return ""; + } + // internal state - bitmap_argb32 m_bitmap; // source bitmap for images - std::string const m_dirname; // directory name of image file (for lazy loading) - std::string const m_imagefile; // name of the image file (for lazy loading) - std::string const m_alphafile; // name of the alpha file (for lazy loading) - bool m_hasalpha = false; // is there any alpha component present? + util::nsvg_image_ptr m_svg; // parsed SVG image + std::shared_ptr<NSVGrasterizer> m_rasterizer; // SVG rasteriser + bitmap_argb32 m_bitmap; // source bitmap for images + bool m_hasalpha = false; // is there any alpha component present? + + // cold state + std::string m_dirname; // directory name of image file (for lazy loading) + std::string m_imagefile; // name of the image file (for lazy loading) + std::string m_alphafile; // name of the alpha file (for lazy loading) + std::string m_data; // embedded image data }; @@ -2881,7 +3075,11 @@ private: { // load the basic bitmap assert(m_file != nullptr); - /*m_hasalpha[number] = */ render_load_png(m_bitmap[number], *m_file[number], m_dirname.c_str(), m_imagefile[number].c_str()); + if (m_file[number]->open(m_dirname + PATH_SEPARATOR + m_imagefile[number]) == osd_file::error::NONE) + { + /*m_hasalpha[number] = */ render_load_png(m_bitmap[number], *m_file[number]); + m_file[number]->close(); + } // load the alpha bitmap if specified //if (m_bitmap[number].valid() && m_alphafile[number]) @@ -3593,7 +3791,7 @@ void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen) if (first) m_bounds = rawbounds; else - union_render_bounds(m_bounds, rawbounds); + m_bounds |= rawbounds; first = false; // accumulate visible screens and their bounds bounds @@ -3602,7 +3800,7 @@ void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen) if (scrfirst) scrbounds = rawbounds; else - union_render_bounds(scrbounds, rawbounds); + scrbounds |= rawbounds; scrfirst = false; // accumulate active screens @@ -3912,10 +4110,13 @@ layout_view::item::item( , m_animoutput(env.device(), make_animoutput_tag(env, itemnode)) , m_have_output(env.get_attribute_string(itemnode, "name", "")[0]) , m_have_animoutput(!make_animoutput_tag(env, itemnode).empty()) + , m_animinput_port(nullptr) + , m_animmask(make_animmask(env, itemnode)) + , m_animshift(get_state_shift(m_animmask)) , m_input_port(nullptr) , m_input_field(nullptr) , m_input_mask(env.get_attribute_int(itemnode, "inputmask", 0)) - , m_input_shift(get_input_shift(m_input_mask)) + , m_input_shift(get_state_shift(m_input_mask)) , m_input_raw(env.get_attribute_bool(itemnode, "inputraw", 0)) , m_clickthrough(env.get_attribute_bool(itemnode, "clickthrough", "yes")) , m_screen(nullptr) @@ -3924,6 +4125,7 @@ layout_view::item::item( , m_blend_mode(get_blend_mode(env, itemnode)) , m_visibility_mask(env.visibility_mask()) , m_input_tag(make_input_tag(env, itemnode)) + , m_animinput_tag(make_animinput_tag(env, itemnode)) , m_rawbounds(make_bounds(env, itemnode, trans)) , m_has_clickthrough(env.get_attribute_string(itemnode, "clickthrough", "")[0]) { @@ -3972,7 +4174,7 @@ render_bounds layout_view::item::bounds() const if (m_bounds.size() == 1U) return m_bounds.front().bounds; else - return interpolate_bounds(m_bounds, m_have_animoutput ? s32(m_animoutput) : state()); + return interpolate_bounds(m_bounds, animation_state()); } @@ -3985,7 +4187,7 @@ render_color layout_view::item::color() const if (m_color.size() == 1U) return m_color.front().color; else - return interpolate_color(m_color, m_have_animoutput ? s32(m_animoutput) : state()); + return interpolate_color(m_color, animation_state()); } @@ -4038,6 +4240,9 @@ void layout_view::item::resolve_tags() if (m_have_animoutput) m_animoutput.resolve(); + if (!m_animinput_tag.empty()) + m_animinput_port = m_element->machine().root_device().ioport(m_animinput_tag); + if (!m_input_tag.empty()) { m_input_port = m_element->machine().root_device().ioport(m_input_tag); @@ -4066,6 +4271,21 @@ void layout_view::item::resolve_tags() // find_element - find element definition //--------------------------------------------- +inline int layout_view::item::animation_state() const +{ + if (m_have_animoutput) + return (s32(m_animoutput) & m_animmask) >> m_animshift; + else if (m_animinput_port) + return (m_animinput_port->read() & m_animmask) >> m_animshift; + else + return state(); +} + + +//--------------------------------------------- +// find_element - find element definition +//--------------------------------------------- + layout_element *layout_view::item::find_element(view_environment &env, util::xml::data_node const &itemnode, element_map &elemmap) { char const *const name(env.get_attribute_string(itemnode, !strcmp(itemnode.get_name(), "element") ? "ref" : "element", nullptr)); @@ -4157,6 +4377,30 @@ std::string layout_view::item::make_animoutput_tag(view_environment &env, util:: //--------------------------------------------- +// make_animmask - get animation state mask +//--------------------------------------------- + +ioport_value layout_view::item::make_animmask(view_environment &env, util::xml::data_node const &itemnode) +{ + util::xml::data_node const *const animate(itemnode.get_child("animate")); + return animate ? env.get_attribute_int(*animate, "mask", ~ioport_value(0)) : ~ioport_value(0); +} + + +//--------------------------------------------- +// make_animinput_tag - get absolute tag for +// animation input +//--------------------------------------------- + +std::string layout_view::item::make_animinput_tag(view_environment &env, util::xml::data_node const &itemnode) +{ + util::xml::data_node const *const animate(itemnode.get_child("animate")); + char const *tag(animate ? env.get_attribute_string(*animate, "inputtag", nullptr) : nullptr); + return tag ? env.device().subtag(tag) : std::string(); +} + + +//--------------------------------------------- // make_input_tag - get absolute input tag //--------------------------------------------- @@ -4200,10 +4444,10 @@ int layout_view::item::get_blend_mode(view_environment &env, util::xml::data_nod //--------------------------------------------- -// get_input_shift - shift to right-align LSB +// get_state_shift - shift to right-align LSB //--------------------------------------------- -unsigned layout_view::item::get_input_shift(ioport_value mask) +unsigned layout_view::item::get_state_shift(ioport_value mask) { unsigned result(0U); while (mask && !BIT(mask, 0)) 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; } diff --git a/src/emu/rendutil.h b/src/emu/rendutil.h index b5c06d6d0fc..c5cdd7d2c4c 100644 --- a/src/emu/rendutil.h +++ b/src/emu/rendutil.h @@ -13,7 +13,7 @@ #pragma once -#include "render.h" +#include "rendertypes.h" #include <cmath> @@ -23,6 +23,8 @@ enum ru_imgformat { RENDUTIL_IMGFORMAT_PNG, + RENDUTIL_IMGFORMAT_JPEG, + RENDUTIL_IMGFORMAT_MSDIB, RENDUTIL_IMGFORMAT_UNKNOWN, RENDUTIL_IMGFORMAT_ERROR @@ -40,9 +42,10 @@ void render_resample_argb_bitmap_hq(bitmap_argb32 &dest, bitmap_argb32 &source, bool render_clip_line(render_bounds *bounds, const render_bounds *clip); bool render_clip_quad(render_bounds *bounds, const render_bounds *clip, render_quad_texuv *texcoords); void render_line_to_quad(const render_bounds *bounds, float width, float length_extension, render_bounds *bounds0, render_bounds *bounds1); -void render_load_jpeg(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, const char *filename); -bool render_load_png(bitmap_argb32 &bitmap, emu_file &file, const char *dirname, const char *filename, bool load_as_alpha_to_existing = false); -ru_imgformat render_detect_image(emu_file &file, const char *dirname, const char *filename); +void render_load_msdib(bitmap_argb32 &bitmap, util::core_file &file); +void render_load_jpeg(bitmap_argb32 &bitmap, util::core_file &file); +bool render_load_png(bitmap_argb32 &bitmap, util::core_file &file, bool load_as_alpha_to_existing = false); +ru_imgformat render_detect_image(util::core_file &file); @@ -62,75 +65,6 @@ static inline float render_round_nearest(float f) /*------------------------------------------------- - set_render_bounds_xy - cleaner way to set the - bounds --------------------------------------------------*/ - -static inline void set_render_bounds_xy(render_bounds &bounds, float x0, float y0, float x1, float y1) -{ - bounds.x0 = x0; - bounds.y0 = y0; - bounds.x1 = x1; - bounds.y1 = y1; -} - - -/*------------------------------------------------- - set_render_bounds_wh - cleaner way to set the - bounds --------------------------------------------------*/ - -static inline void set_render_bounds_wh(render_bounds &bounds, float x0, float y0, float width, float height) -{ - bounds.x0 = x0; - bounds.y0 = y0; - bounds.x1 = x0 + width; - bounds.y1 = y0 + height; -} - - -/*------------------------------------------------- - sect_render_bounds - compute the intersection - of two render_bounds --------------------------------------------------*/ - -static inline void sect_render_bounds(render_bounds &dest, const render_bounds &src) -{ - dest.x0 = (std::max)(dest.x0, src.x0); - dest.x1 = (std::min)(dest.x1, src.x1); - dest.y0 = (std::max)(dest.y0, src.y0); - dest.y1 = (std::min)(dest.y1, src.y1); -} - - -/*------------------------------------------------- - union_render_bounds - compute the union of two - render_bounds --------------------------------------------------*/ - -static inline void union_render_bounds(render_bounds &dest, const render_bounds &src) -{ - dest.x0 = (std::min)(dest.x0, src.x0); - dest.x1 = (std::max)(dest.x1, src.x1); - dest.y0 = (std::min)(dest.y0, src.y0); - dest.y1 = (std::max)(dest.y1, src.y1); -} - - -/*------------------------------------------------- - set_render_color - cleaner way to set a color --------------------------------------------------*/ - -static inline void set_render_color(render_color *color, float a, float r, float g, float b) -{ - color->a = a; - color->r = r; - color->g = g; - color->b = b; -} - - -/*------------------------------------------------- orientation_swap_flips - swap the X and Y flip flags -------------------------------------------------*/ @@ -213,5 +147,4 @@ static inline u8 apply_brightness_contrast_gamma(u8 src, float brightness, float return u8(result * 255.0f); } - #endif // MAME_EMU_RENDUTIL_H diff --git a/src/emu/screen.cpp b/src/emu/screen.cpp index c1e575819db..c265b14e31b 100644 --- a/src/emu/screen.cpp +++ b/src/emu/screen.cpp @@ -9,14 +9,15 @@ ***************************************************************************/ #include "emu.h" +#include "screen.h" + #include "emuopts.h" -#include "png.h" +#include "render.h" #include "rendutil.h" -#include <nanosvg/src/nanosvg.h> -#include <nanosvg/src/nanosvgrast.h> +#include "nanosvg.h" +#include "png.h" -#include <clocale> #include <set> @@ -43,7 +44,6 @@ u32 screen_device::m_id_counter = 0; class screen_device::svg_renderer { public: svg_renderer(memory_region *region); - ~svg_renderer(); int width() const; int height() const; @@ -69,8 +69,8 @@ private: int x0, y0, x1, y1; }; - NSVGimage *m_image; - NSVGrasterizer *m_rasterizer; + util::nsvg_image_ptr m_image; + util::nsvg_rasterizer_ptr m_rasterizer; std::vector<bool> m_key_state; std::vector<std::vector<NSVGshape *>> m_keyed_shapes; std::unordered_map<std::string, int> m_key_ids; @@ -97,8 +97,8 @@ screen_device::svg_renderer::svg_renderer(memory_region *region) const std::unique_ptr<char []> s(new char[region->bytes() + 1]); memcpy(s.get(), region->base(), region->bytes()); s[region->bytes()] = 0; - m_image = nsvgParse(s.get(), "px", 72); - m_rasterizer = nsvgCreateRasterizer(); + m_image.reset(nsvgParse(s.get(), "px", 72)); + m_rasterizer.reset(nsvgCreateRasterizer()); m_key_count = 0; @@ -123,12 +123,6 @@ screen_device::svg_renderer::svg_renderer(memory_region *region) osd_printf_verbose("Parsed SVG '%s', aspect ratio %f\n", region->name(), (m_image->height == 0.0f) ? 0 : m_image->width / m_image->height); } -screen_device::svg_renderer::~svg_renderer() -{ - nsvgDeleteRasterizer(m_rasterizer); - nsvgDelete(m_image); -} - int screen_device::svg_renderer::width() const { return int(m_image->width + 0.5); @@ -150,7 +144,7 @@ void screen_device::svg_renderer::render_state(std::vector<u32> &dest, const std s->flags &= ~NSVG_FLAGS_VISIBLE; } - nsvgRasterize(m_rasterizer, m_image, 0, 0, m_scale, (unsigned char *)&dest[0], m_sx, m_sy, m_sx*4); + nsvgRasterize(m_rasterizer.get(), m_image.get(), 0, 0, m_scale, (unsigned char *)&dest[0], m_sx, m_sy, m_sx*4); // Nanosvg generates non-premultiplied alpha, so remultiply by // alpha to "blend" against a black background. Plus align the @@ -1921,14 +1915,14 @@ void screen_device::finalize_burnin() osd_file::error filerr = file.open(util::string_format("%s" PATH_SEPARATOR "burnin-%s.png", machine().basename(), tag() + 1)); if (filerr == osd_file::error::NONE) { - png_info pnginfo; + util::png_info pnginfo; // add two text entries describing the image pnginfo.add_text("Software", util::string_format("%s %s", emulator_info::get_appname(), emulator_info::get_build_version()).c_str()); pnginfo.add_text("System", util::string_format("%s %s", machine().system().manufacturer, machine().system().type.fullname()).c_str()); // now do the actual work - png_write_bitmap(file, &pnginfo, finalmap, 0, nullptr); + util::png_write_bitmap(file, &pnginfo, finalmap, 0, nullptr); } } @@ -1947,8 +1941,13 @@ void screen_device::load_effect_overlay(const char *filename) fullname.append(".png"); // load the file + m_screen_overlay_bitmap.reset(); emu_file file(machine().options().art_path(), OPEN_FLAG_READ); - render_load_png(m_screen_overlay_bitmap, file, nullptr, fullname.c_str()); + if (file.open(fullname) == osd_file::error::NONE) + { + render_load_png(m_screen_overlay_bitmap, file); + file.close(); + } if (m_screen_overlay_bitmap.valid()) m_container->set_overlay(&m_screen_overlay_bitmap); else diff --git a/src/emu/video.cpp b/src/emu/video.cpp index 6f88d9ae03d..56d60ba1661 100644 --- a/src/emu/video.cpp +++ b/src/emu/video.cpp @@ -329,16 +329,16 @@ void video_manager::save_snapshot(screen_device *screen, emu_file &file) // add two text entries describing the image std::string text1 = std::string(emulator_info::get_appname()).append(" ").append(emulator_info::get_build_version()); std::string text2 = std::string(machine().system().manufacturer).append(" ").append(machine().system().type.fullname()); - png_info pnginfo; + util::png_info pnginfo; pnginfo.add_text("Software", text1.c_str()); pnginfo.add_text("System", text2.c_str()); // now do the actual work const rgb_t *palette = (screen != nullptr && screen->has_palette()) ? screen->palette().palette()->entry_list_adjusted() : nullptr; int entries = (screen != nullptr && screen->has_palette()) ? screen->palette().entries() : 0; - png_error error = png_write_bitmap(file, &pnginfo, m_snap_bitmap, entries, palette); - if (error != PNGERR_NONE) - osd_printf_error("Error generating PNG for snapshot: png_error = %d\n", error); + util::png_error error = util::png_write_bitmap(file, &pnginfo, m_snap_bitmap, entries, palette); + if (error != util::png_error::NONE) + osd_printf_error("Error generating PNG for snapshot: png_error = %d\n", std::underlying_type_t<util::png_error>(error)); } diff --git a/src/frontend/mame/ui/icorender.cpp b/src/frontend/mame/ui/icorender.cpp index bea85f9dd60..5bda0836838 100644 --- a/src/frontend/mame/ui/icorender.cpp +++ b/src/frontend/mame/ui/icorender.cpp @@ -19,6 +19,7 @@ #include "emu.h" #include "icorender.h" +#include "util/msdib.h" #include "util/png.h" #include <algorithm> @@ -26,11 +27,10 @@ #include <cstdint> #include <cstring> -// need to set LOG_OUTPUT_STREAM because there's no logerror outside devices -#define LOG_OUTPUT_STREAM std::cerr +// need to set LOG_OUTPUT_FUNC or LOG_OUTPUT_STREAM because there's no logerror outside devices +#define LOG_OUTPUT_FUNC osd_printf_verbose #define LOG_GENERAL (1U << 0) -#define LOG_DIB (1U << 1) //#define VERBOSE (LOG_GENERAL | LOG_DIB) @@ -41,16 +41,6 @@ namespace ui { namespace { -// DIB compression schemes -enum : uint32_t -{ - DIB_COMP_NONE = 0, - DIB_COMP_RLE8 = 1, - DIB_COMP_RLE4 = 2, - DIB_COMP_BITFIELDS = 3 -}; - - // ICO file header struct icon_dir_t { @@ -83,67 +73,6 @@ struct icon_dir_entry_t uint32_t offset; // offset to image data from start of file }; -// old-style DIB header -struct bitmap_core_header_t -{ - uint32_t size; // size of the header (12, 16 or 64) - int16_t width; // width of bitmap in pixels - int16_t height; // height of the image in pixels - uint16_t planes; // number of colour planes (must be 1) - uint16_t bpp; // bits per pixel -}; - -// new-style DIB header -struct bitmap_info_header_t -{ - uint32_t size; // size of the header - int32_t width; // width of bitmap in pixels - int32_t height; // height of bitmap in pixels - uint16_t planes; // number of colour planes (must be 1) - uint16_t bpp; // bits per pixel - uint32_t comp; // compression method - uint32_t rawsize; // size of bitmap data after decompression or 0 if uncompressed - int32_t hres; // horizontal resolution in pixels/metre - int32_t vres; // horizontal resolution in pixels/metre - uint32_t colors; // number of colours or 0 for 1 << bpp - uint32_t important; // number of important colours or 0 if all important - uint32_t red; // red field mask - must be contiguous - uint32_t green; // green field mask - must be contiguous - uint32_t blue; // blue field mask - must be contiguous - uint32_t alpha; // alpha field mask - must be contiguous -}; - - -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; -} - bool load_ico_png(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb32 &bitmap) { @@ -151,10 +80,10 @@ bool load_ico_png(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb3 if (9U >= dir.size) return false; fp.seek(dir.offset, SEEK_SET); - png_error const err(png_read_bitmap(fp, bitmap)); + util::png_error const err(util::png_read_bitmap(fp, bitmap)); switch (err) { - case PNGERR_NONE: + case util::png_error::NONE: // found valid PNG image assert(bitmap.valid()); if ((dir.get_width() == bitmap.width()) && ((dir.get_height() == bitmap.height()))) @@ -172,7 +101,7 @@ bool load_ico_png(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb3 } return true; - case PNGERR_BAD_SIGNATURE: + case util::png_error::BAD_SIGNATURE: // doesn't look like PNG data - just fall back to DIB without the file header return false; @@ -190,426 +119,37 @@ bool load_ico_png(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb3 bool load_ico_dib(util::core_file &fp, icon_dir_entry_t const &dir, bitmap_argb32 &bitmap) { - // check that these things haven't been padded somehow - static_assert(sizeof(bitmap_core_header_t) == 12U, "compiler has applied padding to bitmap_core_header_t"); - static_assert(sizeof(bitmap_info_header_t) == 56U, "compiler has applied padding to bitmap_info_header_t"); - - // ensure the header fits in the space for the image data - union { bitmap_core_header_t core; bitmap_info_header_t info; } header; - assert(&header.core.size == &header.info.size); - if (sizeof(header.core) > dir.size) - return false; - std::memset(&header, 0, sizeof(header)); fp.seek(dir.offset, SEEK_SET); - if (fp.read(&header.core.size, sizeof(header.core.size)) != sizeof(header.core.size)) - { - LOG( - "Error reading DIB header size from ICO file at offset %u (directory size %u)\n", - dir.offset, - dir.size); - return false; - } - header.core.size = little_endianize_int32(header.core.size); - if (dir.size < header.core.size) - { - LOG( - "ICO file image data at %u (%u bytes) is too small for DIB header (%u bytes)\n", - dir.offset, - dir.size, - header.core.size); - return false; - } - - // identify and read the header - convert OS/2 headers to Windows 3 format - unsigned palette_bytes(4U); - switch (header.core.size) + util::msdib_error const err(util::msdib_read_bitmap_data(fp, bitmap, dir.size, dir.get_height())); + switch (err) { - case 16U: - case 64U: - // extended OS/2 bitmap header with support for compression - LOG( - "ICO image data at %u (%u bytes) uses unsupported OS/2 DIB header (size %u)\n", - dir.offset, - dir.size, - header.core.size); - return false; - - case 12U: - // introduced in OS/2 and Windows 2.0 + case util::msdib_error::NONE: + // found valid DIB image + assert(bitmap.valid()); + if ((dir.get_width() == bitmap.width()) && ((dir.get_height() == bitmap.height()))) { - palette_bytes = 3U; - uint32_t const header_read(std::min<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 ICO file image data at %u (%u bytes)\n", dir.offset, dir.size); - return false; - } - 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 ICO file image data at %u: %d*%d, %u planes, %u bpp\n", - dir.offset, - 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; + LOG("Loaded %d*%d pixel DIB image from ICO file\n", bitmap.width(), bitmap.height()); } - break; - - default: - // the next version will be longer - if (124U >= header.core.size) + else { LOG( - "ICO image data at %u (%u bytes) uses unsupported DIB header format (size %u)\n", - dir.offset, - dir.size, - header.core.size); - return false; - } - // fall through - case 40U: - case 52U: - case 56U: - case 108U: - case 124U: - // the Windows 3 bitmap header with optional extensions - { - palette_bytes = 4U; - uint32_t const header_read(std::min<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 ICO file image data at %u (%u bytes)\n", dir.offset, dir.size); - return false; - } - 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 ICO file image data at %u: %d*%d (%d*%d ppm), %u planes, %u bpp %u/%s%u colors\n", - dir.offset, - 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( - "ICO file DIB image data at %u uses unsupported planes/bits per pixel %u*%u\n", - dir.offset, - header.info.planes, - header.info.bpp); - return false; - } - - // check dimensions - if ((0 >= header.info.width) || (0 == header.info.height)) - { - LOG( - "ICO file DIB image data at %u has invalid dimensions %u*%u\n", - dir.offset, - header.info.width, - header.info.height); - return false; - } - bool const top_down(0 > header.info.height); - if (top_down) - header.info.height = -header.info.height; - bool have_and_mask((2 * dir.get_height()) == header.info.height); - if (!have_and_mask && (dir.get_height() != header.info.height)) - { - osd_printf_verbose( - "ICO file DIB image data at %lu height %ld doesn't match directory height %u with or without AND mask\n", - (unsigned long)dir.offset, - (long)header.info.height, - dir.get_height()); - return false; - } - if (have_and_mask) - header.info.height >>= 1; - - // ensure compression scheme is supported - bool indexed(true), no_palette(false); - 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( - "ICO file DIB image data at %lu has oversized palette with %lu entries for %u bits per pixel\n", - (unsigned long)dir.offset, - (unsigned long)header.info.colors, - (unsigned)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_t, alpha) > header.info.size) - { - osd_printf_verbose( - "ICO file DIB image data at %lu specifies bit masks but is too small (size %lu)\n", - (unsigned long)dir.offset, - (unsigned long)header.info.size); - return false; + "Loaded %d*%d pixel DIB image from ICO file (directory indicated %u*%u)\n", + bitmap.width(), + bitmap.height(), + dir.get_width(), + dir.get_height()); } - break; + return true; default: - LOG("ICO file DIB image data at %u uses unsupported compression scheme %u\n", header.info.comp); - return false; - } - - // we can now calculate the size of the palette and row data - size_t const palette_entries( - indexed - ? ((1U == header.info.bpp) ? 2U : header.info.colors ? header.info.colors : (1U << header.info.bpp)) - : (no_palette ? 0U : header.info.colors)); - size_t const palette_size(palette_bytes * palette_entries); - size_t const row_bytes(((31 + (header.info.width * header.info.bpp)) >> 5) << 2); - size_t const mask_row_bytes(((31 + header.info.width) >> 5) << 2); - 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 > dir.size) - { + // invalid DIB data or I/O error LOG( - "ICO file image data at %u (%u bytes) smaller than calculated DIB data size (%u bytes)\n", + "Error %u reading DIB image data from ICO file at offset %u (directory size %u)\n", + unsigned(err), dir.offset, - dir.size, - required_size); + dir.size); return false; } - - // 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<uint8_t []> palette_data(new uint8_t [palette_size]); - if (fp.read(palette_data.get(), palette_size) != palette_size) - { - LOG("Error reading palette from ICO file DIB image data at %u (%u bytes)\n", dir.offset, dir.size); - return false; - } - size_t const palette_usable(std::min<size_t>(palette_entries, size_t(1) << header.info.bpp)); - palette.reset(new rgb_t [palette_usable]); - uint8_t const *ptr(palette_data.get()); - for (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( - "ICO file DIB image data at %lu specifies non-contiguous channel masks 0x%lx | 0x%lx | 0x%lx | 0x%lx\n", - (unsigned long)dir.offset, - (unsigned long)header.info.red, - (unsigned long)header.info.green, - (unsigned long)header.info.blue, - (unsigned long)header.info.alpha); - } - if ((32U != header.info.bpp) && ((header.info.red | header.info.green | header.info.blue | header.info.alpha) >> header.info.bpp)) - { - LOG( - "ICO file DIB image data at %lu specifies channel masks 0x%x | 0x%x | 0x%x | 0x%x that exceed %u bits per pixel\n", - dir.offset, - header.info.red, - header.info.green, - header.info.blue, - header.info.alpha, - header.info.bpp); - return false; - } - LOGMASKED( - LOG_DIB, - "DIB from ICO file image data at %1$u using channels: R((x >> %3$u) & 0x%4$0*2$x) G((x >> %5$u) & 0x%6$0*2$x) B((x >> %7$u) & 0x%8$0*2$x) A((x >> %9$u) & 0x%10$0*2$x)\n", - dir.offset, - (header.info.bpp + 3) >> 2, - red_shift, - (uint32_t(1) << red_bits) - 1, - green_shift, - (uint32_t(1) << green_bits) - 1, - blue_shift, - (uint32_t(1) << blue_bits) - 1, - alpha_shift, - (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<uint8_t []> row_data(new uint8_t [row_bytes]); - bitmap.allocate(header.info.width, header.info.height); - int const y_inc(top_down ? 1 : -1); - for (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 ICO image data at %u\n", i, dir.offset); - return false; - } - uint8_t *src(row_data.get()); - uint32_t *dest(&bitmap.pix(y)); - unsigned shift(0U); - for (int32_t x = 0; header.info.width > x; ++x, ++dest) - { - // extract or compose a pixel - 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 |= uint32_t(*src) << shift; - } - - // convert to RGB - if (indexed) - { - if (palette_entries > pix) - { - *dest = palette[pix]; - } - else - { - *dest = rgb_t::transparent(); - osd_printf_verbose( - "ICO file DIB image data at %lu has out-of-range color %lu at (%ld, %ld) with %lu palette entries\n", - (unsigned long)dir.offset, - (unsigned long)pix, - (long)x, - (long)y, - (unsigned long)palette_entries); - } - } - else - { - uint8_t r(dib_splat_sample((pix >> red_shift) & ((uint32_t(1) << red_bits) - 1), red_bits)); - uint8_t g(dib_splat_sample((pix >> green_shift) & ((uint32_t(1) << green_bits) - 1), green_bits)); - uint8_t b(dib_splat_sample((pix >> blue_shift) & ((uint32_t(1) << blue_bits) - 1), blue_bits)); - uint8_t a(dib_splat_sample((pix >> alpha_shift) & ((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 (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 ICO image data at %u\n", i, dir.offset); - return false; - } - uint8_t *src(row_data.get()); - uint32_t *dest(&bitmap.pix(y)); - unsigned shift(0U); - for (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 true; } diff --git a/src/frontend/mame/ui/menu.cpp b/src/frontend/mame/ui/menu.cpp index b1bcbb908b7..aa7d90f0ea3 100644 --- a/src/frontend/mame/ui/menu.cpp +++ b/src/frontend/mame/ui/menu.cpp @@ -92,10 +92,17 @@ menu::global_state::global_state(running_machine &machine, ui_options const &opt { m_bgrnd_bitmap = std::make_unique<bitmap_argb32>(0, 0); emu_file backgroundfile(".", OPEN_FLAG_READ); - render_load_jpeg(*m_bgrnd_bitmap, backgroundfile, nullptr, "background.jpg"); + if (backgroundfile.open("background.jpg") == osd_file::error::NONE) + { + render_load_jpeg(*m_bgrnd_bitmap, backgroundfile); + backgroundfile.close(); + } - if (!m_bgrnd_bitmap->valid()) - render_load_png(*m_bgrnd_bitmap, backgroundfile, nullptr, "background.png"); + if (!m_bgrnd_bitmap->valid() && (backgroundfile.open("background.png") == osd_file::error::NONE)) + { + render_load_png(*m_bgrnd_bitmap, backgroundfile); + backgroundfile.close(); + } if (m_bgrnd_bitmap->valid()) m_bgrnd_texture->set_bitmap(*m_bgrnd_bitmap, m_bgrnd_bitmap->cliprect(), TEXFORMAT_ARGB32); diff --git a/src/frontend/mame/ui/selmenu.cpp b/src/frontend/mame/ui/selmenu.cpp index b05991b02e9..7a652c1b6bf 100644 --- a/src/frontend/mame/ui/selmenu.cpp +++ b/src/frontend/mame/ui/selmenu.cpp @@ -95,6 +95,62 @@ char const *const hover_msg[] = { __("Show DATs view"), }; + +void load_image(bitmap_argb32 &bitmap, emu_file &file, std::string const &base) +{ + if (file.open(base + ".png") == osd_file::error::NONE) + { + render_load_png(bitmap, file); + file.close(); + } + + if (!bitmap.valid() && (file.open(base + ".jpg") == osd_file::error::NONE)) + { + render_load_jpeg(bitmap, file); + file.close(); + } + + if (!bitmap.valid() && (file.open(base + ".bmp") == osd_file::error::NONE)) + { + render_load_msdib(bitmap, file); + file.close(); + } +} + + +void load_driver_image(bitmap_argb32 &bitmap, emu_file &file, game_driver const &driver) +{ + // try to load snapshot first from saved "0000.png" file + std::string fullname = driver.name; + load_image(bitmap, file, fullname + PATH_SEPARATOR + "0000"); + + // if fail, attempt to load from standard file + if (!bitmap.valid()) + load_image(bitmap, file, fullname); + + // if fail again, attempt to load from parent file + if (!bitmap.valid()) + { + // ignore BIOS sets + bool isclone = strcmp(driver.parent, "0") != 0; + if (isclone) + { + int const cx = driver_list::find(driver.parent); + if ((0 <= cx) && (driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT)) + isclone = false; + } + + if (isclone) + { + fullname = driver.parent; + load_image(bitmap, file, fullname + PATH_SEPARATOR + "0000"); + + if (!bitmap.valid()) + load_image(bitmap, file, fullname); + } + } +} + } // anonymous namespace constexpr std::size_t menu_select_launch::MAX_VISIBLE_SEARCH; // stupid non-inline semantics @@ -2226,41 +2282,16 @@ void menu_select_launch::arts_render(float origx1, float origy1, float origx2, f if (software->startempty == 1) { // Load driver snapshot - std::string fullname = std::string(software->driver->name) + ".png"; - render_load_png(tmp_bitmap, snapfile, nullptr, fullname.c_str()); - - if (!tmp_bitmap.valid()) - { - fullname.assign(software->driver->name).append(".jpg"); - render_load_jpeg(tmp_bitmap, snapfile, nullptr, fullname.c_str()); - } + load_driver_image(tmp_bitmap, snapfile, *software->driver); } else { // First attempt from name list - std::string pathname = software->listname; - std::string fullname = software->shortname + ".png"; - render_load_png(tmp_bitmap, snapfile, pathname.c_str(), fullname.c_str()); + load_image(tmp_bitmap, snapfile, software->listname + PATH_SEPARATOR + software->shortname); + // Second attempt from driver name + part name if (!tmp_bitmap.valid()) - { - fullname.assign(software->shortname).append(".jpg"); - render_load_jpeg(tmp_bitmap, snapfile, pathname.c_str(), fullname.c_str()); - } - - if (!tmp_bitmap.valid()) - { - // Second attempt from driver name + part name - pathname.assign(software->driver->name).append(software->part); - fullname.assign(software->shortname).append(".png"); - render_load_png(tmp_bitmap, snapfile, pathname.c_str(), fullname.c_str()); - - if (!tmp_bitmap.valid()) - { - fullname.assign(software->shortname).append(".jpg"); - render_load_jpeg(tmp_bitmap, snapfile, pathname.c_str(), fullname.c_str()); - } - } + load_image(tmp_bitmap, snapfile, software->driver->name + software->part + PATH_SEPARATOR + software->shortname); } m_cache->set_snapx_software(software); @@ -2285,51 +2316,7 @@ void menu_select_launch::arts_render(float origx1, float origy1, float origx2, f { emu_file snapfile(searchstr, OPEN_FLAG_READ); bitmap_argb32 tmp_bitmap; - - // try to load snapshot first from saved "0000.png" file - std::string fullname(driver->name); - render_load_png(tmp_bitmap, snapfile, fullname.c_str(), "0000.png"); - - if (!tmp_bitmap.valid()) - render_load_jpeg(tmp_bitmap, snapfile, fullname.c_str(), "0000.jpg"); - - // if fail, attemp to load from standard file - if (!tmp_bitmap.valid()) - { - fullname.assign(driver->name).append(".png"); - render_load_png(tmp_bitmap, snapfile, nullptr, fullname.c_str()); - - if (!tmp_bitmap.valid()) - { - fullname.assign(driver->name).append(".jpg"); - render_load_jpeg(tmp_bitmap, snapfile, nullptr, fullname.c_str()); - } - } - - // if fail again, attemp to load from parent file - if (!tmp_bitmap.valid()) - { - // set clone status - bool cloneof = strcmp(driver->parent, "0"); - if (cloneof) - { - int cx = driver_list::find(driver->parent); - if ((cx >= 0) && (driver_list::driver(cx).flags & machine_flags::IS_BIOS_ROOT)) - cloneof = false; - } - - if (cloneof) - { - fullname.assign(driver->parent).append(".png"); - render_load_png(tmp_bitmap, snapfile, nullptr, fullname.c_str()); - - if (!tmp_bitmap.valid()) - { - fullname.assign(driver->parent).append(".jpg"); - render_load_jpeg(tmp_bitmap, snapfile, nullptr, fullname.c_str()); - } - } - } + load_driver_image(tmp_bitmap, snapfile, *driver); m_cache->set_snapx_driver(driver); m_switch_image = false; 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 diff --git a/src/mame/drivers/alg.cpp b/src/mame/drivers/alg.cpp index 73189abc79e..9e42a1ce7e5 100644 --- a/src/mame/drivers/alg.cpp +++ b/src/mame/drivers/alg.cpp @@ -30,7 +30,6 @@ #include "machine/ldp1450.h" #include "machine/nvram.h" #include "machine/amigafdc.h" -#include "render.h" #include "speaker.h" diff --git a/src/mame/drivers/by17.cpp b/src/mame/drivers/by17.cpp index b8832806c8d..33074d0e79b 100644 --- a/src/mame/drivers/by17.cpp +++ b/src/mame/drivers/by17.cpp @@ -26,7 +26,6 @@ ToDo: #include "by17.lh" #include "by17_pwerplay.lh" #include "by17_matahari.lh" -#include "render.h" class by17_state : public genpin_class { @@ -980,10 +979,6 @@ void by17_state::machine_reset() { genpin_class::machine_reset(); - render_target *target = machine().render().first_target(); - - target->set_view(0); - m_u10a = 0; m_u10b = 0; m_u11a = 0; diff --git a/src/mame/drivers/by35.cpp b/src/mame/drivers/by35.cpp index e4f3aab38a6..70d15834ce1 100644 --- a/src/mame/drivers/by35.cpp +++ b/src/mame/drivers/by35.cpp @@ -73,7 +73,6 @@ ToDo: #include "machine/6821pia.h" #include "machine/timer.h" #include "audio/bally.h" -#include "render.h" #include "speaker.h" //#define VERBOSE 1 @@ -1441,10 +1440,6 @@ void by35_state::machine_reset() { genpin_class::machine_reset(); - render_target *target = machine().render().first_target(); - - target->set_view(0); - m_u10a = 0; m_u10b = 0; m_u11a = 0; diff --git a/src/mame/drivers/cliffhgr.cpp b/src/mame/drivers/cliffhgr.cpp index 3761c0548b7..339866ab13c 100644 --- a/src/mame/drivers/cliffhgr.cpp +++ b/src/mame/drivers/cliffhgr.cpp @@ -83,7 +83,6 @@ Side 2 = 0x8F7DDD (or 0x880000 | ( 0x77 << 12 ) | 0x0DDD) #include "video/tms9928a.h" #include "machine/nvram.h" -#include "render.h" #include "speaker.h" diff --git a/src/mame/drivers/decwritr.cpp b/src/mame/drivers/decwritr.cpp index b5b32b2ee71..84133c2e05c 100644 --- a/src/mame/drivers/decwritr.cpp +++ b/src/mame/drivers/decwritr.cpp @@ -19,7 +19,7 @@ #include "machine/i8251.h" #include "machine/input_merger.h" #include "sound/beep.h" -#include "render.h" +#include "screen.h" #include "speaker.h" diff --git a/src/mame/drivers/dlair.cpp b/src/mame/drivers/dlair.cpp index b914c58b35c..496ad516322 100644 --- a/src/mame/drivers/dlair.cpp +++ b/src/mame/drivers/dlair.cpp @@ -46,7 +46,6 @@ #include "sound/ay8910.h" #include "sound/spkrdev.h" #include "emupal.h" -#include "render.h" #include "speaker.h" #include "dlair.lh" diff --git a/src/mame/drivers/istellar.cpp b/src/mame/drivers/istellar.cpp index 687be6f0e79..1f546d3be55 100644 --- a/src/mame/drivers/istellar.cpp +++ b/src/mame/drivers/istellar.cpp @@ -26,7 +26,6 @@ Todo: #include "machine/gen_latch.h" #include "machine/ldv1000.h" #include "emupal.h" -#include "render.h" #include "speaker.h" diff --git a/src/mame/drivers/lgp.cpp b/src/mame/drivers/lgp.cpp index 67bc14514f8..9b76c5469de 100644 --- a/src/mame/drivers/lgp.cpp +++ b/src/mame/drivers/lgp.cpp @@ -69,7 +69,6 @@ Dumping Notes: #include "cpu/z80/z80.h" #include "machine/ldv1000.h" #include "emupal.h" -#include "render.h" #include "speaker.h" diff --git a/src/mame/layout/unkeinv.lay b/src/mame/layout/unkeinv.lay index 12b42eef798..b5fde8e2ac8 100644 --- a/src/mame/layout/unkeinv.lay +++ b/src/mame/layout/unkeinv.lay @@ -6,24 +6,6 @@ license:CC0 <!-- define elements --> - <element name="black"><rect><color red="0" green="0" blue="0" /></rect></element> - - <element name="wandl"> - <rect> - <bounds x="0" y="0" width="18" height="1" /> - <color red="0" green="0" blue="0" /> - </rect> - <disk> - <bounds state="0" x="0" y="0" width="1" height="1" /> - <bounds state="11" x="16.5" y="0" width="1" height="1" /> - <color red="1.0" green="0.2" blue="0.23" /> - </disk> - </element> - <element name="wandm" defstate="0"> - <rect state="0"><color red="0.1" green="0.1" blue="0.1" /></rect> - <rect state="1"><color red="1.0" green="1.0" blue="1.0" /></rect> - </element> - <element name="ledy" defstate="0"> <disk state="0"><color red="0.07" green="0.06" blue="0.02" /></disk> <disk state="1"><color red="1.0" green="0.95" blue="0.2" /></disk> @@ -32,6 +14,11 @@ license:CC0 <disk state="0"><color red="0.023" green="0.1" blue="0.02" /></disk> <disk state="1"><color red="0.23" green="1.0" blue="0.2" /></disk> </element> + <element name="ledr" defstate="0"> + <disk state="0"><color red="0.1" green="0.02" blue="0.023" /></disk> + <disk state="1"><color red="1.0" green="0.2" blue="0.23" /></disk> + </element> + <!-- build screen --> @@ -41,21 +28,24 @@ license:CC0 <!-- wand --> - <repeat count="9"> + <repeat count="4"> <param name="y" start="1" increment="3" /> - <element ref="black" inputtag="IN.1" inputmask="0x00" inputraw="yes"><bounds x="0" y="~y~" width="18" height="1" /></element> - <element ref="wandl" inputtag="IN.1" inputmask="0x0f" inputraw="yes"><bounds x="0" y="~y~" width="18" height="1" /></element> + <param name="n" start="0" increment="1" /> + <element ref="ledr" name="~n~.7"> + <animate inputtag="IN.1" mask="0x0f" /> + <bounds state="0" x="0" y="~y~" width="1" height="1" /> + <bounds state="11" x="16.5" y="~y~" width="1" height="1" /> + </element> + </repeat> + <repeat count="5"> + <param name="y" start="13" increment="3" /> + <param name="n" start="0" increment="1" /> + <element ref="ledr" name="~n~.6"> + <animate inputtag="IN.1" mask="0x0f" /> + <bounds state="0" x="0" y="~y~" width="1" height="1" /> + <bounds state="11" x="16.5" y="~y~" width="1" height="1" /> + </element> </repeat> - - <element name="0.7" ref="wandm" blend="multiply"><bounds x="-0.5" y="0.5" width="19" height="2" /></element> - <element name="1.7" ref="wandm" blend="multiply"><bounds x="-0.5" y="3.5" width="19" height="2" /></element> - <element name="2.7" ref="wandm" blend="multiply"><bounds x="-0.5" y="6.5" width="19" height="2" /></element> - <element name="3.7" ref="wandm" blend="multiply"><bounds x="-0.5" y="9.5" width="19" height="2" /></element> - <element name="0.6" ref="wandm" blend="multiply"><bounds x="-0.5" y="12.5" width="19" height="2" /></element> - <element name="1.6" ref="wandm" blend="multiply"><bounds x="-0.5" y="15.5" width="19" height="2" /></element> - <element name="2.6" ref="wandm" blend="multiply"><bounds x="-0.5" y="18.5" width="19" height="2" /></element> - <element name="3.6" ref="wandm" blend="multiply"><bounds x="-0.5" y="21.5" width="19" height="2" /></element> - <element name="4.6" ref="wandm" blend="multiply"><bounds x="-0.5" y="24.5" width="19" height="2" /></element> <!-- invaders --> diff --git a/src/mame/video/midtunit.cpp b/src/mame/video/midtunit.cpp index 94594884e38..4a74af2b188 100644 --- a/src/mame/video/midtunit.cpp +++ b/src/mame/video/midtunit.cpp @@ -1018,7 +1018,7 @@ void midtunit_video_device::log_bitmap(int command, int bpp, bool Skip) } } - png_write_bitmap(file, nullptr, m_log_bitmap, 0, nullptr); + util::png_write_bitmap(file, nullptr, m_log_bitmap, 0, nullptr); if (m_log_json) { diff --git a/src/mame/video/ssv.cpp b/src/mame/video/ssv.cpp index 47f9f031654..c8629284845 100644 --- a/src/mame/video/ssv.cpp +++ b/src/mame/video/ssv.cpp @@ -135,7 +135,6 @@ #include "emu.h" #include "includes/ssv.h" -#include "render.h" void ssv_state::drawgfx_line(bitmap_ind16 &bitmap, const rectangle &cliprect, int gfx, uint32_t code, uint32_t color, int flipx, int flipy, int base_sx, int base_sy, int shadow, int realline, int line) diff --git a/src/mame/video/st0020.cpp b/src/mame/video/st0020.cpp index 6702174635a..f378dbbc302 100644 --- a/src/mame/video/st0020.cpp +++ b/src/mame/video/st0020.cpp @@ -16,7 +16,9 @@ #include "emu.h" #include "st0020.h" -#include "render.h" + +#include "screen.h" + DEFINE_DEVICE_TYPE(ST0020_SPRITES, st0020_device, "st0020", "Seta ST0020 Sprites") diff --git a/src/mame/video/tx1.cpp b/src/mame/video/tx1.cpp index 306da618218..08b74e80462 100644 --- a/src/mame/video/tx1.cpp +++ b/src/mame/video/tx1.cpp @@ -7,10 +7,10 @@ ****************************************************************************/ #include "emu.h" -#include "render.h" +#include "includes/tx1.h" + #include "video/resnet.h" #include "cpu/i86/i86.h" -#include "includes/tx1.h" #define OBJ_FRAC 16 diff --git a/src/osd/modules/render/bgfx/texturemanager.cpp b/src/osd/modules/render/bgfx/texturemanager.cpp index a03a6b46125..d539d1aae46 100644 --- a/src/osd/modules/render/bgfx/texturemanager.cpp +++ b/src/osd/modules/render/bgfx/texturemanager.cpp @@ -59,7 +59,11 @@ bgfx_texture* texture_manager::create_png_texture(std::string path, std::string { bitmap_argb32 bitmap; emu_file file(path.c_str(), OPEN_FLAG_READ); - render_load_png(bitmap, file, nullptr, file_name.c_str()); + if (file.open(file_name) == osd_file::error::NONE) + { + render_load_png(bitmap, file); + file.close(); + } if (bitmap.width() == 0 || bitmap.height() == 0) { diff --git a/src/osd/modules/render/d3d/d3dhlsl.cpp b/src/osd/modules/render/d3d/d3dhlsl.cpp index e19f8882232..57ab4943adb 100644 --- a/src/osd/modules/render/d3d/d3dhlsl.cpp +++ b/src/osd/modules/render/d3d/d3dhlsl.cpp @@ -359,14 +359,14 @@ void shaders::render_snapshot(IDirect3DSurface9 *surface) // add two text entries describing the image std::string text1 = std::string(emulator_info::get_appname()).append(" ").append(emulator_info::get_build_version()); std::string text2 = std::string(machine->system().manufacturer).append(" ").append(machine->system().type.fullname()); - png_info pnginfo; + util::png_info pnginfo; pnginfo.add_text("Software", text1.c_str()); pnginfo.add_text("System", text2.c_str()); // now do the actual work - png_error error = png_write_bitmap(file, &pnginfo, snapshot, 1 << 24, nullptr); - if (error != PNGERR_NONE) - osd_printf_error("Error generating PNG for HLSL snapshot: png_error = %d\n", error); + util::png_error error = util::png_write_bitmap(file, &pnginfo, snapshot, 1 << 24, nullptr); + if (error != util::png_error::NONE) + osd_printf_error("Error generating PNG for HLSL snapshot: png_error = %d\n", std::underlying_type_t<util::png_error>(error)); result = snap_copy_target->UnlockRect(); if (FAILED(result)) @@ -721,7 +721,11 @@ int shaders::create_resources() osd_printf_verbose("Direct3D: Error %08lX during device SetRenderTarget call\n", result); emu_file file(machine->options().art_path(), OPEN_FLAG_READ); - render_load_png(shadow_bitmap, file, nullptr, options->shadow_mask_texture); + if (file.open(options->shadow_mask_texture) == osd_file::error::NONE) + { + render_load_png(shadow_bitmap, file); + file.close(); + } // experimental: if we have a shadow bitmap, create a texture for it if (shadow_bitmap.valid()) @@ -742,7 +746,11 @@ int shaders::create_resources() d3d->get_texture_manager()->m_texture_list.push_back(std::move(tex)); } - render_load_png(lut_bitmap, file, nullptr, options->lut_texture); + if (file.open(options->lut_texture) == osd_file::error::NONE) + { + render_load_png(lut_bitmap, file); + file.close(); + } if (lut_bitmap.valid()) { render_texinfo texture; @@ -761,7 +769,11 @@ int shaders::create_resources() d3d->get_texture_manager()->m_texture_list.push_back(std::move(tex)); } - render_load_png(ui_lut_bitmap, file, nullptr, options->ui_lut_texture); + if (file.open(options->ui_lut_texture) == osd_file::error::NONE) + { + render_load_png(ui_lut_bitmap, file); + file.close(); + } if (ui_lut_bitmap.valid()) { render_texinfo texture; diff --git a/src/tools/pngcmp.cpp b/src/tools/pngcmp.cpp index e490d1c6811..714700a3824 100644 --- a/src/tools/pngcmp.cpp +++ b/src/tools/pngcmp.cpp @@ -74,7 +74,7 @@ static int generate_png_diff(const std::string& imgfile1, const std::string& img int width, height, maxwidth; util::core_file::ptr file; osd_file::error filerr; - png_error pngerr; + util::png_error pngerr; int error = 100; /* open the source image */ @@ -86,11 +86,11 @@ static int generate_png_diff(const std::string& imgfile1, const std::string& img } /* load the source image */ - pngerr = png_read_bitmap(*file, bitmap1); + pngerr = util::png_read_bitmap(*file, bitmap1); file.reset(); - if (pngerr != PNGERR_NONE) + if (pngerr != util::png_error::NONE) { - printf("Could not read %s (%d)\n", imgfile1.c_str(), pngerr); + printf("Could not read %s (%d)\n", imgfile1.c_str(), int(pngerr)); goto error; } @@ -103,11 +103,11 @@ static int generate_png_diff(const std::string& imgfile1, const std::string& img } /* load the source image */ - pngerr = png_read_bitmap(*file, bitmap2); + pngerr = util::png_read_bitmap(*file, bitmap2); file.reset(); - if (pngerr != PNGERR_NONE) + if (pngerr != util::png_error::NONE) { - printf("Could not read %s (%d)\n", imgfile2.c_str(), pngerr); + printf("Could not read %s (%d)\n", imgfile2.c_str(), int(pngerr)); goto error; } @@ -176,11 +176,11 @@ static int generate_png_diff(const std::string& imgfile1, const std::string& img printf("Could not open %s (%d)\n", outfilename.c_str(), int(filerr)); goto error; } - pngerr = png_write_bitmap(*file, nullptr, finalbitmap, 0, nullptr); + pngerr = util::png_write_bitmap(*file, nullptr, finalbitmap, 0, nullptr); file.reset(); - if (pngerr != PNGERR_NONE) + if (pngerr != util::png_error::NONE) { - printf("Could not write %s (%d)\n", outfilename.c_str(), pngerr); + printf("Could not write %s (%d)\n", outfilename.c_str(), int(pngerr)); goto error; } } diff --git a/src/tools/regrep.cpp b/src/tools/regrep.cpp index 5bebc733197..49637c2eb16 100644 --- a/src/tools/regrep.cpp +++ b/src/tools/regrep.cpp @@ -748,7 +748,7 @@ static int compare_screenshots(summary_file *curfile) /* if that worked, load the file */ if (filerr == osd_file::error::NONE) { - png_read_bitmap(*file, bitmaps[listnum]); + util::png_read_bitmap(*file, bitmaps[listnum]); file.reset(); } } @@ -835,7 +835,7 @@ static int generate_png_diff(const summary_file *curfile, std::string &destdir, int bitmapcount = 0; util::core_file::ptr file; osd_file::error filerr; - png_error pngerr; + util::png_error pngerr; int error = -1; int starty; @@ -855,9 +855,9 @@ static int generate_png_diff(const summary_file *curfile, std::string &destdir, goto error; /* load the source image */ - pngerr = png_read_bitmap(*file, bitmaps[bitmapcount++]); + pngerr = util::png_read_bitmap(*file, bitmaps[bitmapcount++]); file.reset(); - if (pngerr != PNGERR_NONE) + if (pngerr != util::png_error::NONE) goto error; } @@ -924,9 +924,9 @@ static int generate_png_diff(const summary_file *curfile, std::string &destdir, filerr = util::core_file::open(dstfilename, OPEN_FLAG_WRITE | OPEN_FLAG_CREATE, file); if (filerr != osd_file::error::NONE) goto error; - pngerr = png_write_bitmap(*file, nullptr, finalbitmap, 0, nullptr); + pngerr = util::png_write_bitmap(*file, nullptr, finalbitmap, 0, nullptr); file.reset(); - if (pngerr != PNGERR_NONE) + if (pngerr != util::png_error::NONE) goto error; /* if we get here, we are error free */ |