diff options
author | 2019-07-06 00:23:20 +1000 | |
---|---|---|
committer | 2019-07-06 00:23:20 +1000 | |
commit | c38a3395e94e38a37cf6b8efdf9f49a5c9d415df (patch) | |
tree | 8ad5f6ee37953b4d19241ce31d11da23a712cc3e /src/emu/rendlay.cpp | |
parent | 7ee50ffec7afabd20a027f89e47f11b116301071 (diff) |
Make layout format more flexible:
* There is no longer a concept of "layers" - there are only screens and elements.
* Elements are now instantiated with <element ref="...">
* Screens and elements can have explicit blending mode specified with blend="..."
* Default blending mode for screens is "add" and default for other elements is "alpha"
* Other supported modes are "none" and "multiply"
* This removes the options to enable/disable layers individually - use views instead
* Legacy layouts can still be loaded, and support won't be removed for at least a year
The current artwork model is over-stretched. It's based on a Space
Invaders cabinet model, and isn't applicable to a lot of the systems
MAME emulates now. The fact that MAME has to switch to an "alternate"
mode to deal with games like Golly! Ghost! without requiring pre-matted
bitmaps shows that the Space Invaders model wasn't even adequate for
general arcade use. It shows in that for a lot of the systems that
heavily depend on artwork, people just seem to randomly choose layers
for elements until they get something that works. Also, the fact that
MAME will switch to an alternate (Golly! Ghost!) mode depending on the
combination of elements is a trap for people learning to make artwork.
There are cases that the current approach of implying the blending mode
from the layer doesn't work with. Examples include LEDs behind
diffusers (requires additive blending for layout elements), and mutliple
stacked LCD panels (requires RGB multiplication for screens).
For configurability, it's now a lot easier to make multiple views using
groups. For example, if you want to make it possible to hide the
control panel section of your layout, you can put the control panel
elements in a group and create views with and without it.
I will gradually migrate the internal artwork to use the new approach.
I have an XSLT stylesheet that helps with this, but I'm not comfortable
adding it because it isn't a complete solution and it still requires
manul steps.
I wanted to get the re-worked pointer handling done sooner so I could
push them both at the same time, but unfortunately various things have
prevented me from progressing as quickly as I wanted to. Sorry guys,
that stuff's going to have to wait.
Diffstat (limited to 'src/emu/rendlay.cpp')
-rw-r--r-- | src/emu/rendlay.cpp | 296 |
1 files changed, 182 insertions, 114 deletions
diff --git a/src/emu/rendlay.cpp b/src/emu/rendlay.cpp index d083ba5510a..8fc785b74a7 100644 --- a/src/emu/rendlay.cpp +++ b/src/emu/rendlay.cpp @@ -2944,6 +2944,9 @@ void layout_element::component::apply_skew(bitmap_argb32 &dest, int skewwidth) // LAYOUT VIEW //************************************************************************** +struct layout_view::layer_lists { item_list backdrops, screens, overlays, bezels, cpanels, marquees; }; + + //------------------------------------------------- // layout_view - constructor //------------------------------------------------- @@ -2956,11 +2959,41 @@ layout_view::layout_view( : m_name(make_name(env, viewnode)) , m_aspect(1.0f) , m_scraspect(1.0f) + , m_items() + , m_has_art(false) { + // parse the layout m_expbounds.x0 = m_expbounds.y0 = m_expbounds.x1 = m_expbounds.y1 = 0; environment local(env); + layer_lists layers; local.set_parameter("viewname", std::string(m_name)); - add_items(local, viewnode, elemmap, groupmap, ROT0, identity_transform, render_color{ 1.0F, 1.0F, 1.0F, 1.0F }, true, false, true); + add_items(layers, local, viewnode, elemmap, groupmap, ROT0, identity_transform, render_color{ 1.0F, 1.0F, 1.0F, 1.0F }, true, false, true); + + // deal with legacy element groupings + if ((layers.backdrops.size() > 1) && layers.overlays.empty()) + { + // multiple backdrop pieces and no overlays (Golly! Ghost! mode): + // backdrop (alpha) + screens (add) + bezels (alpha) + cpanels (alpha) + marquees (alpha) + m_items.splice(m_items.end(), layers.backdrops); + m_items.splice(m_items.end(), layers.screens); + m_items.splice(m_items.end(), layers.bezels); + m_items.splice(m_items.end(), layers.cpanels); + m_items.splice(m_items.end(), layers.marquees); + } + else + { + // screens (add) + overlays (RGB multiply) + backdrop (add) + bezels (alpha) + cpanels (alpha) + marquees (alpha) + for (item &backdrop : layers.backdrops) + backdrop.set_blend_mode(BLENDMODE_ADD); + m_items.splice(m_items.end(), layers.screens); + m_items.splice(m_items.end(), layers.overlays); + m_items.splice(m_items.end(), layers.backdrops); + m_items.splice(m_items.end(), layers.bezels); + m_items.splice(m_items.end(), layers.cpanels); + m_items.splice(m_items.end(), layers.marquees); + } + + // calculate metrics recompute(render_layer_config()); for (group_map::value_type &group : groupmap) group.second.set_bounds_unresolved(); @@ -2977,25 +3010,6 @@ layout_view::~layout_view() //------------------------------------------------- -// items - return the appropriate list -//------------------------------------------------- - -layout_view::item_list &layout_view::items(item_layer layer) -{ - switch (layer) - { - case ITEM_LAYER_BACKDROP: return m_backdrop_list; - case ITEM_LAYER_SCREEN: return m_screen_list; - case ITEM_LAYER_OVERLAY: return m_overlay_list; - case ITEM_LAYER_BEZEL: return m_bezel_list; - case ITEM_LAYER_CPANEL: return m_cpanel_list; - case ITEM_LAYER_MARQUEE: return m_marquee_list; - default: throw false; // calling this with an invalid layer is bad, m'kay? - } -} - - -//------------------------------------------------- // recompute - recompute the bounds and aspect // ratio of a view and all of its contained items //------------------------------------------------- @@ -3010,43 +3024,27 @@ void layout_view::recompute(render_layer_config layerconfig) // loop over all layers bool first = true; bool scrfirst = true; - for (item_layer layer = ITEM_LAYER_FIRST; layer < ITEM_LAYER_MAX; ++layer) + for (item &curitem : m_items) { - // determine if this layer should be visible - switch (layer) - { - case ITEM_LAYER_BACKDROP: m_layenabled[layer] = layerconfig.backdrops_enabled(); break; - case ITEM_LAYER_OVERLAY: m_layenabled[layer] = layerconfig.overlays_enabled(); break; - case ITEM_LAYER_BEZEL: m_layenabled[layer] = layerconfig.bezels_enabled(); break; - case ITEM_LAYER_CPANEL: m_layenabled[layer] = layerconfig.cpanels_enabled(); break; - case ITEM_LAYER_MARQUEE: m_layenabled[layer] = layerconfig.marquees_enabled(); break; - default: m_layenabled[layer] = true; break; - } - - // only do it if requested - if (m_layenabled[layer]) - for (item &curitem : items(layer)) - { - // accumulate bounds - if (first) - m_bounds = curitem.m_rawbounds; - else - union_render_bounds(m_bounds, curitem.m_rawbounds); - first = false; + // accumulate bounds + if (first) + m_bounds = curitem.m_rawbounds; + else + union_render_bounds(m_bounds, curitem.m_rawbounds); + first = false; - // accumulate screen bounds - if (curitem.m_screen) - { - if (scrfirst) - m_scrbounds = curitem.m_rawbounds; - else - union_render_bounds(m_scrbounds, curitem.m_rawbounds); - scrfirst = false; + // accumulate screen bounds + if (curitem.m_screen) + { + if (scrfirst) + m_scrbounds = curitem.m_rawbounds; + else + union_render_bounds(m_scrbounds, curitem.m_rawbounds); + scrfirst = false; - // accumulate the screens in use while we're scanning - m_screens.add(*curitem.m_screen); - } - } + // accumulate the screens in use while we're scanning + m_screens.add(*curitem.m_screen); + } } // if we have an explicit bounds, override it @@ -3085,14 +3083,13 @@ void layout_view::recompute(render_layer_config layerconfig) float yscale = (target_bounds.y1 - target_bounds.y0) / (m_bounds.y1 - m_bounds.y0); // normalize all the item bounds - for (item_layer layer = ITEM_LAYER_FIRST; layer < ITEM_LAYER_MAX; ++layer) - for (item &curitem : items(layer)) - { - curitem.m_bounds.x0 = target_bounds.x0 + (curitem.m_rawbounds.x0 - xoffs) * xscale; - curitem.m_bounds.x1 = target_bounds.x0 + (curitem.m_rawbounds.x1 - xoffs) * xscale; - curitem.m_bounds.y0 = target_bounds.y0 + (curitem.m_rawbounds.y0 - yoffs) * yscale; - curitem.m_bounds.y1 = target_bounds.y0 + (curitem.m_rawbounds.y1 - yoffs) * yscale; - } + for (item &curitem : items()) + { + curitem.m_bounds.x0 = target_bounds.x0 + (curitem.m_rawbounds.x0 - xoffs) * xscale; + curitem.m_bounds.x1 = target_bounds.x0 + (curitem.m_rawbounds.x1 - xoffs) * xscale; + curitem.m_bounds.y0 = target_bounds.y0 + (curitem.m_rawbounds.y0 - yoffs) * yscale; + curitem.m_bounds.y1 = target_bounds.y0 + (curitem.m_rawbounds.y1 - yoffs) * yscale; + } } @@ -3102,13 +3099,8 @@ void layout_view::recompute(render_layer_config layerconfig) void layout_view::resolve_tags() { - for (item_layer layer = ITEM_LAYER_FIRST; layer < ITEM_LAYER_MAX; ++layer) - { - for (item &curitem : items(layer)) - { - curitem.resolve_tags(); - } - } + for (item &curitem : items()) + curitem.resolve_tags(); } @@ -3117,6 +3109,7 @@ void layout_view::resolve_tags() //------------------------------------------------- void layout_view::add_items( + layer_lists &layers, environment &env, util::xml::data_node const &parentnode, element_map &elemmap, @@ -3154,27 +3147,37 @@ void layout_view::add_items( } else if (!strcmp(itemnode->get_name(), "backdrop")) { - m_backdrop_list.emplace_back(env, *itemnode, elemmap, orientation, trans, color); + layers.backdrops.emplace_back(env, *itemnode, elemmap, orientation, trans, color); + m_has_art = true; } else if (!strcmp(itemnode->get_name(), "screen")) { - m_screen_list.emplace_back(env, *itemnode, elemmap, orientation, trans, color); + layers.screens.emplace_back(env, *itemnode, elemmap, orientation, trans, color); + } + else if (!strcmp(itemnode->get_name(), "element")) + { + layers.screens.emplace_back(env, *itemnode, elemmap, orientation, trans, color); + m_has_art = true; } else if (!strcmp(itemnode->get_name(), "overlay")) { - m_overlay_list.emplace_back(env, *itemnode, elemmap, orientation, trans, color); + layers.overlays.emplace_back(env, *itemnode, elemmap, orientation, trans, color); + m_has_art = true; } else if (!strcmp(itemnode->get_name(), "bezel")) { - m_bezel_list.emplace_back(env, *itemnode, elemmap, orientation, trans, color); + layers.bezels.emplace_back(env, *itemnode, elemmap, orientation, trans, color); + m_has_art = true; } else if (!strcmp(itemnode->get_name(), "cpanel")) { - m_cpanel_list.emplace_back(env, *itemnode, elemmap, orientation, trans, color); + layers.cpanels.emplace_back(env, *itemnode, elemmap, orientation, trans, color); + m_has_art = true; } else if (!strcmp(itemnode->get_name(), "marquee")) { - m_marquee_list.emplace_back(env, *itemnode, elemmap, orientation, trans, color); + layers.marquees.emplace_back(env, *itemnode, elemmap, orientation, trans, color); + m_has_art = true; } else if (!strcmp(itemnode->get_name(), "group")) { @@ -3205,6 +3208,7 @@ void layout_view::add_items( environment local(env); add_items( + layers, local, found->second.get_groupnode(), elemmap, @@ -3224,7 +3228,7 @@ void layout_view::add_items( environment local(env); for (int i = 0; count > i; ++i) { - add_items(local, *itemnode, elemmap, groupmap, orientation, trans, color, false, true, !i); + add_items(layers, local, *itemnode, elemmap, groupmap, orientation, trans, color, false, true, !i); local.increment_parameters(); } } @@ -3277,30 +3281,21 @@ layout_view::item::item( int orientation, layout_group::transform const &trans, render_color const &color) - : m_element(nullptr) + : m_element(find_element(env, itemnode, elemmap)) , m_output(env.device(), env.get_attribute_string(itemnode, "name", "")) , m_have_output(env.get_attribute_string(itemnode, "name", "")[0]) - , m_input_tag(env.get_attribute_string(itemnode, "inputtag", "")) + , m_input_tag(make_input_tag(env, itemnode)) , m_input_port(nullptr) - , m_input_mask(0) + , m_input_field(nullptr) + , m_input_mask(env.get_attribute_int(itemnode, "inputmask", 0)) , m_input_shift(0) - , m_input_raw(false) + , m_input_raw(0 != env.get_attribute_int(itemnode, "inputraw", 0)) , m_screen(nullptr) , m_orientation(orientation_add(env.parse_orientation(itemnode.get_child("orientation")), orientation)) + , m_rawbounds(make_bounds(env, itemnode, trans)) , m_color(render_color_multiply(env.parse_color(itemnode.get_child("color")), color)) + , m_blend_mode(get_blend_mode(env, itemnode)) { - // find the associated element - char const *const name(env.get_attribute_string(itemnode, "element", nullptr)); - if (name) - { - // search the list of elements for a match, error if not found - element_map::iterator const found(elemmap.find(name)); - if (elemmap.end() != found) - m_element = &found->second; - else - throw layout_syntax_error(util::string_format("unable to find element %s", name)); - } - // outputs need resolving if (m_have_output) m_output.resolve(); @@ -3309,17 +3304,10 @@ layout_view::item::item( int index = env.get_attribute_int(itemnode, "index", -1); if (index != -1) m_screen = screen_device_iterator(env.machine().root_device()).byindex(index); - m_input_mask = env.get_attribute_int(itemnode, "inputmask", 0); - for (u32 mask = m_input_mask; (mask != 0) && (~mask & 1); mask >>= 1) m_input_shift++; - m_input_raw = env.get_attribute_int(itemnode, "inputraw", 0) == 1; + for (u32 mask = m_input_mask; (mask != 0) && (~mask & 1); mask >>= 1) + m_input_shift++; if (m_have_output && m_element) m_output = m_element->default_state(); - env.parse_bounds(itemnode.get_child("bounds"), m_rawbounds); - render_bounds_transform(m_rawbounds, trans); - if (m_rawbounds.x0 > m_rawbounds.x1) - std::swap(m_rawbounds.x0, m_rawbounds.x1); - if (m_rawbounds.y0 > m_rawbounds.y1) - std::swap(m_rawbounds.y0, m_rawbounds.y1); // sanity checks if (strcmp(itemnode.get_name(), "screen") == 0) @@ -3340,9 +3328,6 @@ layout_view::item::item( { throw layout_syntax_error(util::string_format("item of type %s require an element tag", itemnode.get_name())); } - - if (has_input()) - m_input_port = env.device().ioport(m_input_tag.c_str()); } @@ -3382,17 +3367,17 @@ int layout_view::item::state() const else if (!m_input_tag.empty()) { // if configured to an input, fetch the input value - if (m_input_port) + if (m_input_raw) { - if (m_input_raw) - { + if (m_input_port) return (m_input_port->read() & m_input_mask) >> m_input_shift; - } - else + } + else + { + if (m_input_field) { - ioport_field const *const field = m_input_port->field(m_input_mask); - if (field) - return ((m_input_port->read() ^ field->defvalue()) & m_input_mask) ? 1 : 0; + assert(m_input_port); + return ((m_input_port->read() ^ m_input_field->defvalue()) & m_input_mask) ? 1 : 0; } } } @@ -3405,13 +3390,96 @@ int layout_view::item::state() const // resolve_tags - resolve tags, if any are set //--------------------------------------------- - void layout_view::item::resolve_tags() { - if (has_input()) + if (!m_input_tag.empty()) { m_input_port = m_element->machine().root_device().ioport(m_input_tag.c_str()); + if (m_input_port) + m_input_field = m_input_port->field(m_input_mask); + } +} + + +//--------------------------------------------- +// find_element - find element definition +//--------------------------------------------- + +layout_element *layout_view::item::find_element(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)); + if (!name) + return nullptr; + + // search the list of elements for a match, error if not found + element_map::iterator const found(elemmap.find(name)); + if (elemmap.end() != found) + return &found->second; + else + throw layout_syntax_error(util::string_format("unable to find element %s", name)); +} + + +//--------------------------------------------- +// make_bounds - get transformed bounds +//--------------------------------------------- + +render_bounds layout_view::item::make_bounds( + environment &env, + util::xml::data_node const &itemnode, + layout_group::transform const &trans) +{ + render_bounds bounds; + env.parse_bounds(itemnode.get_child("bounds"), bounds); + render_bounds_transform(bounds, trans); + if (bounds.x0 > bounds.x1) + std::swap(bounds.x0, bounds.x1); + if (bounds.y0 > bounds.y1) + std::swap(bounds.y0, bounds.y1); + return bounds; +} + + +//--------------------------------------------- +// make_input_tag - get absolute input tag +//--------------------------------------------- + +std::string layout_view::item::make_input_tag(environment &env, util::xml::data_node const &itemnode) +{ + char const *tag(env.get_attribute_string(itemnode, "inputtag", nullptr)); + return tag ? env.device().subtag(tag) : std::string(); +} + + +//--------------------------------------------- +// get_blend_mode - explicit or implicit blend +//--------------------------------------------- + +int layout_view::item::get_blend_mode(environment &env, util::xml::data_node const &itemnode) +{ + // see if there's a blend mode attribute + char const *const mode(env.get_attribute_string(itemnode, "blend", nullptr)); + if (mode) + { + if (!strcmp(mode, "none")) + return BLENDMODE_NONE; + else if (!strcmp(mode, "alpha")) + return BLENDMODE_ALPHA; + else if (!strcmp(mode, "multiply")) + return BLENDMODE_RGB_MULTIPLY; + else if (!strcmp(mode, "add")) + return BLENDMODE_ADD; + else + throw layout_syntax_error(util::string_format("unknown blend mode %s", mode)); } + + // fall back to implicit blend mode based on element type + if (!strcmp(itemnode.get_name(), "screen")) + return BLENDMODE_ADD; + else if (!strcmp(itemnode.get_name(), "overlay")) + return BLENDMODE_RGB_MULTIPLY; + else + return BLENDMODE_ALPHA; } |