diff options
Diffstat (limited to 'src/emu/rendlay.cpp')
-rw-r--r-- | src/emu/rendlay.cpp | 448 |
1 files changed, 346 insertions, 102 deletions
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)) |