summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu/rendlay.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/rendlay.cpp')
-rw-r--r--src/emu/rendlay.cpp3862
1 files changed, 2859 insertions, 1003 deletions
diff --git a/src/emu/rendlay.cpp b/src/emu/rendlay.cpp
index 23a60c57e13..489f232a1ad 100644
--- a/src/emu/rendlay.cpp
+++ b/src/emu/rendlay.cpp
@@ -2,29 +2,37 @@
// copyright-holders:Aaron Giles, Vas Crabb
/***************************************************************************
- rendlay.c
+ rendlay.cpp
Core rendering layout parser and manager.
***************************************************************************/
#include "emu.h"
+#include "render.h"
+#include "rendlay.h"
#include "emuopts.h"
-#include "render.h"
+#include "fileio.h"
+#include "main.h"
#include "rendfont.h"
-#include "rendlay.h"
#include "rendutil.h"
-#include "vecstream.h"
-#include "xmlfile.h"
+#include "video/rgbutil.h"
+
+#include "util/nanosvg.h"
+#include "util/path.h"
+#include "util/unicode.h"
+#include "util/vecstream.h"
+#include "util/xmlfile.h"
-#include <ctype.h>
+#include <cctype>
#include <algorithm>
#include <cmath>
#include <cstddef>
#include <cstdio>
#include <cstring>
#include <iomanip>
+#include <limits>
#include <locale>
#include <sstream>
#include <stdexcept>
@@ -32,15 +40,29 @@
#include <type_traits>
#include <utility>
+#define LOG_GROUP_BOUNDS_RESOLUTION (1U << 1)
+#define LOG_INTERACTIVE_ITEMS (1U << 2)
+#define LOG_DISK_DRAW (1U << 3)
+#define LOG_IMAGE_LOAD (1U << 4)
+
+//#define VERBOSE (LOG_GROUP_BOUNDS_RESOLUTION | LOG_INTERACTIVE_ITEMS | LOG_DISK_DRAW | LOG_IMAGE_LOAD)
+#define LOG_OUTPUT_FUNC osd_printf_verbose
+#include "logmacro.h"
+
/***************************************************************************
STANDARD LAYOUTS
***************************************************************************/
+#include "layout/generic.h"
+
// screenless layouts
#include "noscreens.lh"
+// single screen layouts
+#include "monitors.lh"
+
// dual screen layouts
#include "dualhsxs.lh"
#include "dualhovu.lh"
@@ -68,14 +90,12 @@ enum
LINE_CAP_END = 2
};
-std::locale const f_portable_locale("C");
-
constexpr layout_group::transform identity_transform{{ {{ 1.0F, 0.0F, 0.0F }}, {{ 0.0F, 1.0F, 0.0F }}, {{ 0.0F, 0.0F, 1.0F }} }};
//**************************************************************************
-// INLINE HELPERS
+// HELPERS
//**************************************************************************
inline void render_bounds_transform(render_bounds &bounds, layout_group::transform const &trans)
@@ -87,9 +107,27 @@ inline void render_bounds_transform(render_bounds &bounds, layout_group::transfo
(bounds.x1 * trans[1][0]) + (bounds.y1 * trans[1][1]) + trans[1][2] };
}
-constexpr render_color render_color_multiply(render_color const &x, render_color const &y)
+inline void alpha_blend(u32 &dest, u32 a, u32 r, u32 g, u32 b, u32 inva)
{
- return render_color{ x.a * y.a, x.r * y.r, x.g * y.g, x.b * y.b };
+ rgb_t const dpix(dest);
+ u32 const da(dpix.a());
+ u32 const finala((a * 255) + (da * inva));
+ u32 const finalr(r + (u32(dpix.r()) * da * inva));
+ u32 const finalg(g + (u32(dpix.g()) * da * inva));
+ u32 const finalb(b + (u32(dpix.b()) * da * inva));
+ dest = rgb_t(finala / 255, finalr / finala, finalg / finala, finalb / finala);
+}
+
+inline void alpha_blend(u32 &dest, render_color const &c, float fill)
+{
+ u32 const a(c.a * fill * 255.0F);
+ if (a)
+ {
+ u32 const r(u32(c.r * (255.0F * 255.0F)) * a);
+ u32 const g(u32(c.g * (255.0F * 255.0F)) * a);
+ u32 const b(u32(c.b * (255.0F * 255.0F)) * a);
+ alpha_blend(dest, a, r, g, b, 255 - a);
+ }
}
@@ -104,7 +142,7 @@ class layout_reference_error : public std::out_of_range { using std::out_of_rang
} // anonymous namespace
-namespace emu { namespace render { namespace detail {
+namespace emu::render::detail {
class layout_environment
{
@@ -117,6 +155,16 @@ private:
, m_text(std::move(t))
, m_text_valid(true)
{ }
+ entry(std::string &&name, std::string_view t)
+ : m_name(std::move(name))
+ , m_text(t)
+ , m_text_valid(true)
+ { }
+ entry(std::string &&name, const char *t)
+ : m_name(std::move(name))
+ , m_text(t)
+ , m_text_valid(true)
+ { }
entry(std::string &&name, s64 i)
: m_name(std::move(name))
, m_int(i)
@@ -177,12 +225,18 @@ private:
{
if (m_float_valid)
{
- m_text = std::to_string(m_float);
+ std::ostringstream stream;
+ stream.imbue(std::locale::classic());
+ stream << m_float;
+ m_text = std::move(stream).str();
m_text_valid = true;
}
else if (m_int_valid)
{
- m_text = std::to_string(m_int);
+ std::ostringstream stream;
+ stream.imbue(std::locale::classic());
+ stream << m_int;
+ m_text = std::move(stream).str();
m_text_valid = true;
}
}
@@ -204,7 +258,7 @@ private:
if (m_text_valid && !m_float_valid)
{
std::istringstream stream(m_text);
- stream.imbue(f_portable_locale);
+ stream.imbue(std::locale::classic());
if (m_text[0] == '$')
{
stream.get();
@@ -240,7 +294,7 @@ private:
if (m_text_valid && !m_int_valid && !m_float_valid)
{
std::istringstream stream(m_text);
- stream.imbue(f_portable_locale);
+ stream.imbue(std::locale::classic());
if (m_text[0] == '$')
{
stream.get();
@@ -299,7 +353,7 @@ private:
if (m_text_valid && !m_int_valid)
{
std::istringstream stream(m_text);
- stream.imbue(f_portable_locale);
+ stream.imbue(std::locale::classic());
if (m_text[0] == '$')
{
stream.get();
@@ -348,7 +402,20 @@ private:
using entry_vector = std::vector<entry>;
- template <typename T, typename U>
+ template <typename T>
+ void try_insert(std::string &&name, T &&value)
+ {
+ entry_vector::iterator const pos(
+ std::lower_bound(
+ m_entries.begin(),
+ m_entries.end(),
+ name,
+ [] (entry const &lhs, auto const &rhs) { return lhs.name() < rhs; }));
+ if ((m_entries.end() == pos) || (pos->name() != name))
+ m_entries.emplace(pos, std::move(name), std::forward<T>(value));
+ }
+
+ template <typename T, typename U, typename = std::enable_if_t<std::is_constructible_v<std::string, T>>>
void try_insert(T &&name, U &&value)
{
entry_vector::iterator const pos(
@@ -358,7 +425,7 @@ private:
name,
[] (entry const &lhs, auto const &rhs) { return lhs.name() < rhs; }));
if ((m_entries.end() == pos) || (pos->name() != name))
- m_entries.emplace(pos, std::forward<T>(name), std::forward<U>(value));
+ m_entries.emplace(pos, std::string(name), std::forward<U>(value));
}
template <typename T, typename U>
@@ -386,7 +453,7 @@ private:
try_insert("deviceshortname", device().shortname());
util::ovectorstream tmp;
unsigned i(0U);
- for (screen_device const &screen : screen_device_iterator(machine().root_device()))
+ for (screen_device const &screen : screen_device_enumerator(machine().root_device()))
{
std::pair<u64, u64> const physaspect(screen.physical_aspect());
s64 const w(screen.visible_area().width()), h(screen.visible_area().height());
@@ -395,33 +462,27 @@ private:
tmp.seekp(0);
util::stream_format(tmp, "scr%uphysicalxaspect", i);
- tmp.put('\0');
- try_insert(&tmp.vec()[0], s64(physaspect.first));
+ try_insert(util::buf_to_string_view(tmp), s64(physaspect.first));
tmp.seekp(0);
util::stream_format(tmp, "scr%uphysicalyaspect", i);
- tmp.put('\0');
- try_insert(&tmp.vec()[0], s64(physaspect.second));
+ try_insert(util::buf_to_string_view(tmp), s64(physaspect.second));
tmp.seekp(0);
util::stream_format(tmp, "scr%unativexaspect", i);
- tmp.put('\0');
- try_insert(&tmp.vec()[0], xaspect);
+ try_insert(util::buf_to_string_view(tmp), xaspect);
tmp.seekp(0);
util::stream_format(tmp, "scr%unativeyaspect", i);
- tmp.put('\0');
- try_insert(&tmp.vec()[0], yaspect);
+ try_insert(util::buf_to_string_view(tmp), yaspect);
tmp.seekp(0);
util::stream_format(tmp, "scr%uwidth", i);
- tmp.put('\0');
- try_insert(&tmp.vec()[0], w);
+ try_insert(util::buf_to_string_view(tmp), w);
tmp.seekp(0);
util::stream_format(tmp, "scr%uheight", i);
- tmp.put('\0');
- try_insert(&tmp.vec()[0], h);
+ try_insert(util::buf_to_string_view(tmp), h);
++i;
}
@@ -429,112 +490,124 @@ private:
}
}
- entry *find_entry(char const *begin, char const *end)
+ entry *find_entry(std::string_view str)
{
cache_device_entries();
entry_vector::iterator const pos(
std::lower_bound(
m_entries.begin(),
m_entries.end(),
- std::make_pair(begin, end - begin),
- [] (entry const &lhs, std::pair<char const *, std::ptrdiff_t> const &rhs)
- { return 0 > std::strncmp(lhs.name().c_str(), rhs.first, rhs.second); }));
- if ((m_entries.end() != pos) && (pos->name().length() == (end - begin)) && !std::strncmp(pos->name().c_str(), begin, end - begin))
+ str,
+ [] (entry const &lhs, std::string_view const &rhs) { return lhs.name() < rhs; }));
+ if ((m_entries.end() != pos) && pos->name() == str)
return &*pos;
else
- return m_next ? m_next->find_entry(begin, end) : nullptr;
+ return m_next ? m_next->find_entry(str) : nullptr;
}
- template <typename... T>
- std::tuple<char const *, char const *, bool> get_variable_text(T &&... args)
+ std::pair<std::string_view, bool> get_variable_text(std::string_view str)
{
- entry *const found(find_entry(std::forward<T>(args)...));
+ entry *const found(find_entry(str));
if (found)
{
- std::string const &text(found->get_text());
- char const *const begin(text.c_str());
- return std::make_tuple(begin, begin + text.length(), true);
+ return std::make_pair(std::string_view(found->get_text()), true);
}
else
{
- return std::make_tuple(nullptr, nullptr, false);
+ return std::make_pair(std::string_view(), false);
}
}
- std::pair<char const *, char const *> expand(char const *begin, char const *end)
+ std::string_view expand(std::string_view str)
{
+ constexpr char variable_start_char = '~';
+ constexpr char variable_end_char = '~';
+
// search for candidate variable references
- char const *start(begin);
- char const *pos(std::find_if(start, end, is_variable_start));
- while (pos != end)
+ std::string_view::size_type start(0);
+ for (std::string_view::size_type pos = str.find_first_of(variable_start_char); pos != std::string_view::npos; )
{
- char const *const term(std::find_if(pos + 1, end, [] (char ch) { return !is_variable_char(ch); }));
- if ((term == end) || !is_variable_end(*term))
+ auto term = std::find_if_not(str.begin() + pos + 1, str.end(), is_variable_char);
+ if ((term == str.end()) || (*term != variable_end_char))
{
// not a valid variable name - keep searching
- pos = std::find_if(term, end, is_variable_start);
+ pos = str.find_first_of(variable_start_char, term - str.begin());
}
else
{
// looks like a variable reference - try to look it up
- std::tuple<char const *, char const *, bool> const text(get_variable_text(pos + 1, term));
- if (std::get<2>(text))
+ std::pair<std::string_view, bool> const text(get_variable_text(str.substr(pos + 1, term - (str.begin() + pos + 1))));
+ if (text.second)
{
// variable found
- if (begin == start)
+ if (start == 0)
m_buffer.seekp(0);
- m_buffer.write(start, pos - start);
- m_buffer.write(std::get<0>(text), std::get<1>(text) - std::get<0>(text));
- start = term + 1;
- pos = std::find_if(start, end, is_variable_start);
+ assert(start < str.length());
+ m_buffer.write(&str[start], pos - start);
+ m_buffer.write(text.first.data(), text.first.length());
+ start = term - str.begin() + 1;
+ pos = str.find_first_of(variable_start_char, start);
}
else
{
// variable not found - move on
- pos = std::find_if(pos + 1, end, is_variable_start);
+ pos = str.find_first_of(variable_start_char, pos + 1);
}
}
}
// short-circuit the case where no substitutions were made
- if (start == begin)
+ if (start == 0)
{
- return std::make_pair(begin, end);
+ return str;
}
else
{
- m_buffer.write(start, pos - start);
- m_buffer.put('\0');
- std::vector<char> const &vec(m_buffer.vec());
- if (vec.empty())
- return std::make_pair(nullptr, nullptr);
- else
- return std::make_pair(&vec[0], &vec[0] + vec.size() - 1);
+ if (start < str.length())
+ m_buffer.write(&str[start], str.length() - start);
+ return util::buf_to_string_view(m_buffer);
}
}
- std::pair<char const *, char const *> expand(char const *str)
+ static constexpr unsigned hex_prefix(std::string_view s)
{
- return expand(str, str + strlen(str));
+ return ((0 != s.length()) && (s[0] == '$')) ? 1U : ((2 <= s.length()) && (s[0] == '0') && ((s[1] == 'x') || (s[1] == 'X'))) ? 2U : 0U;
}
-
- std::string parameter_name(util::xml::data_node const &node)
+ static constexpr unsigned dec_prefix(std::string_view s)
{
- char const *const attrib(node.get_attribute_string("name", nullptr));
- if (!attrib)
- throw layout_syntax_error("parameter lacks name attribute");
- std::pair<char const *, char const *> const expanded(expand(attrib));
- return std::string(expanded.first, expanded.second);
+ return ((0 != s.length()) && (s[0] == '#')) ? 1U : 0U;
}
- static constexpr bool is_variable_start(char ch)
+ int parse_int(std::string_view s, int defvalue)
{
- return '~' == ch;
+ std::istringstream stream;
+ stream.imbue(std::locale::classic());
+ int result;
+ unsigned const hexprefix = hex_prefix(s);
+ if (hexprefix)
+ {
+ stream.str(std::string(s.substr(hexprefix)));
+ unsigned uvalue;
+ stream >> std::hex >> uvalue;
+ result = int(uvalue);
+ }
+ else
+ {
+ stream.str(std::string(s.substr(dec_prefix(s))));
+ stream >> result;
+ }
+
+ return stream ? result : defvalue;
}
- static constexpr bool is_variable_end(char ch)
+
+ std::string parameter_name(util::xml::data_node const &node)
{
- return '~' == ch;
+ std::string const *const attrib(node.get_attribute_string_ptr("name"));
+ if (!attrib)
+ throw layout_syntax_error("parameter lacks name attribute");
+ return std::string(expand(*attrib));
}
+
static constexpr bool is_variable_char(char ch)
{
return (('0' <= ch) && ('9' >= ch)) || (('A' <= ch) && ('Z' >= ch)) || (('a' <= ch) && ('z' >= ch)) || ('_' == ch);
@@ -542,18 +615,37 @@ private:
entry_vector m_entries;
util::ovectorstream m_buffer;
+ std::shared_ptr<NSVGrasterizer> const m_svg_rasterizer;
device_t &m_device;
+ char const *const m_search_path;
+ char const *const m_directory_name;
layout_environment *const m_next = nullptr;
bool m_cached = false;
public:
- explicit layout_environment(device_t &device) : m_device(device) { }
- explicit layout_environment(layout_environment &next) : m_device(next.m_device), m_next(&next) { }
+ layout_environment(device_t &device, char const *searchpath, char const *dirname)
+ : m_svg_rasterizer(nsvgCreateRasterizer(), util::nsvg_deleter())
+ , m_device(device)
+ , m_search_path(searchpath)
+ , m_directory_name(dirname)
+ {
+ }
+ explicit layout_environment(layout_environment &next)
+ : m_svg_rasterizer(next.m_svg_rasterizer)
+ , m_device(next.m_device)
+ , m_search_path(next.m_search_path)
+ , m_directory_name(next.m_directory_name)
+ , 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(); }
+ char const *search_path() const { return m_search_path; }
+ char const *directory_name() const { return m_directory_name; }
+ std::shared_ptr<NSVGrasterizer> const &svg_rasterizer() const { return m_svg_rasterizer; }
void set_parameter(std::string &&name, std::string &&value)
{
@@ -576,20 +668,19 @@ public:
std::string name(parameter_name(node));
if (node.has_attribute("start") || node.has_attribute("increment") || node.has_attribute("lshift") || node.has_attribute("rshift"))
throw layout_syntax_error("start/increment/lshift/rshift attributes are only allowed for repeat parameters");
- char const *const value(node.get_attribute_string("value", nullptr));
+ std::string const *const value(node.get_attribute_string_ptr("value"));
if (!value)
throw layout_syntax_error("parameter lacks value attribute");
// expand value and stash
- std::pair<char const *, char const *> const expanded(expand(value));
- set(std::move(name), std::string(expanded.first, expanded.second));
+ set(std::move(name), std::string(expand(*value)));
}
void set_repeat_parameter(util::xml::data_node const &node, bool init)
{
// two types are allowed here - static value, and start/increment/lshift/rshift
std::string name(parameter_name(node));
- char const *const start(node.get_attribute_string("start", nullptr));
+ std::string const *const start(node.get_attribute_string_ptr("start"));
if (start)
{
// simple validity checks
@@ -603,15 +694,15 @@ public:
// increment is more complex - it may be an integer or a floating-point number
s64 intincrement(0);
double floatincrement(0);
- char const *const increment(node.get_attribute_string("increment", nullptr));
+ std::string const *const increment(node.get_attribute_string_ptr("increment"));
if (increment)
{
- std::pair<char const *, char const *> const expanded(expand(increment));
- unsigned const hexprefix((expanded.first[0] == '$') ? 1U : ((expanded.first[0] == '0') && ((expanded.first[1] == 'x') || (expanded.first[1] == 'X'))) ? 2U : 0U);
- unsigned const decprefix((expanded.first[0] == '#') ? 1U : 0U);
- bool const floatchars(std::find_if(expanded.first, expanded.second, [] (char ch) { return ('.' == ch) || ('e' == ch) || ('E' == ch); }) != expanded.second);
- std::istringstream stream(std::string(expanded.first + hexprefix + decprefix, expanded.second));
- stream.imbue(f_portable_locale);
+ std::string_view const expanded(expand(*increment));
+ unsigned const hexprefix(hex_prefix(expanded));
+ unsigned const decprefix(dec_prefix(expanded));
+ bool const floatchars(expanded.find_first_of(".eE") != std::string_view::npos);
+ std::istringstream stream(std::string(expanded.substr(hexprefix + decprefix)));
+ stream.imbue(std::locale::classic());
if (!hexprefix && !decprefix && floatchars)
{
stream >> floatincrement;
@@ -644,11 +735,10 @@ public:
if ((m_entries.end() != pos) && (pos->name() == name))
throw layout_syntax_error("generator parameters must be defined exactly once per scope");
- std::pair<char const *, char const *> const expanded(expand(start));
if (floatincrement)
- m_entries.emplace(pos, std::move(name), std::string(expanded.first, expanded.second), floatincrement, lshift - rshift);
+ m_entries.emplace(pos, std::move(name), std::string(expand(*start)), floatincrement, lshift - rshift);
else
- m_entries.emplace(pos, std::move(name), std::string(expanded.first, expanded.second), intincrement, lshift - rshift);
+ m_entries.emplace(pos, std::move(name), std::string(expand(*start)), intincrement, lshift - rshift);
}
}
else if (node.has_attribute("increment") || node.has_attribute("lshift") || node.has_attribute("rshift"))
@@ -657,10 +747,9 @@ public:
}
else
{
- char const *const value(node.get_attribute_string("value", nullptr));
+ std::string const *const value(node.get_attribute_string_ptr("value"));
if (!value)
throw layout_syntax_error("parameter lacks value attribute");
- std::pair<char const *, char const *> const expanded(expand(value));
entry_vector::iterator const pos(
std::lower_bound(
m_entries.begin(),
@@ -668,11 +757,11 @@ public:
name,
[] (entry const &lhs, auto const &rhs) { return lhs.name() < rhs; }));
if ((m_entries.end() == pos) || (pos->name() != name))
- m_entries.emplace(pos, std::move(name), std::string(expanded.first, expanded.second));
+ m_entries.emplace(pos, std::move(name), std::string(expand(*value)));
else if (pos->is_generator())
throw layout_syntax_error("generator parameters must be defined exactly once per scope");
else
- pos->set(std::string(expanded.first, expanded.second));
+ pos->set(std::string(expand(*value)));
}
}
@@ -692,95 +781,98 @@ public:
m_entries.end());
}
- char const *get_attribute_string(util::xml::data_node const &node, char const *name, char const *defvalue)
+ std::string_view get_attribute_string(util::xml::data_node const &node, char const *name, std::string_view defvalue = std::string_view())
{
- char const *const attrib(node.get_attribute_string(name, nullptr));
- return attrib ? expand(attrib).first : defvalue;
+ std::string const *const attrib(node.get_attribute_string_ptr(name));
+ return attrib ? expand(*attrib) : defvalue;
+ }
+
+ std::string get_attribute_subtag(util::xml::data_node const &node, char const *name)
+ {
+ std::string const *const attrib(node.get_attribute_string_ptr(name));
+ return attrib ? device().subtag(expand(*attrib)) : std::string();
}
int get_attribute_int(util::xml::data_node const &node, const char *name, int defvalue)
{
- char const *const attrib(node.get_attribute_string(name, nullptr));
+ std::string const *const attrib(node.get_attribute_string_ptr(name));
if (!attrib)
return defvalue;
// similar to what XML nodes do
- std::pair<char const *, char const *> const expanded(expand(attrib));
- std::istringstream stream;
- stream.imbue(f_portable_locale);
- int result;
- if (expanded.first[0] == '$')
- {
- stream.str(std::string(expanded.first + 1, expanded.second));
- unsigned uvalue;
- stream >> std::hex >> uvalue;
- result = int(uvalue);
- }
- else if ((expanded.first[0] == '0') && ((expanded.first[1] == 'x') || (expanded.first[1] == 'X')))
- {
- stream.str(std::string(expanded.first + 2, expanded.second));
- unsigned uvalue;
- stream >> std::hex >> uvalue;
- result = int(uvalue);
- }
- else if (expanded.first[0] == '#')
- {
- stream.str(std::string(expanded.first + 1, expanded.second));
- stream >> result;
- }
- else
- {
- stream.str(std::string(expanded.first, expanded.second));
- stream >> result;
- }
-
- return stream ? result : defvalue;
+ return parse_int(expand(*attrib), defvalue);
}
float get_attribute_float(util::xml::data_node const &node, char const *name, float defvalue)
{
- char const *const attrib(node.get_attribute_string(name, nullptr));
+ std::string const *const attrib(node.get_attribute_string_ptr(name));
if (!attrib)
return defvalue;
// similar to what XML nodes do
- std::pair<char const *, char const *> const expanded(expand(attrib));
- std::istringstream stream(std::string(expanded.first, expanded.second));
- stream.imbue(f_portable_locale);
+ std::istringstream stream(std::string(expand(*attrib)));
+ stream.imbue(std::locale::classic());
float result;
return (stream >> result) ? result : defvalue;
}
+ bool get_attribute_bool(util::xml::data_node const &node, char const *name, bool defvalue)
+ {
+ std::string const *const attrib(node.get_attribute_string_ptr(name));
+ if (!attrib)
+ return defvalue;
+
+ // first try yes/no strings
+ std::string_view const expanded(expand(*attrib));
+ if ("yes" == expanded || "true" == expanded)
+ return true;
+ if ("no" == expanded || "false" == expanded)
+ return false;
+
+ // fall back to integer parsing
+ return parse_int(expanded, defvalue ? 1 : 0) != 0;
+ }
+
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
+ {
+ 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"))
{
- // 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);
+ 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
@@ -826,25 +918,335 @@ public:
case 270: result = ROT270; break;
default: throw layout_syntax_error(util::string_format("invalid rotate attribute %d", rotate));
}
- if (!std::strcmp("yes", get_attribute_string(*node, "swapxy", "no")))
+ if (get_attribute_bool(*node, "swapxy", false))
result ^= ORIENTATION_SWAP_XY;
- if (!std::strcmp("yes", get_attribute_string(*node, "flipx", "no")))
+ if (get_attribute_bool(*node, "flipx", false))
result ^= ORIENTATION_FLIP_X;
- if (!std::strcmp("yes", get_attribute_string(*node, "flipy", "no")))
+ if (get_attribute_bool(*node, "flipy", false))
result ^= ORIENTATION_FLIP_Y;
return result;
}
};
-} } } // namespace emu::render::detail
+
+class view_environment : public layout_environment
+{
+private:
+ view_environment *const m_next_view = nullptr;
+ char const *const m_name;
+ u32 const m_visibility_mask = 0U;
+ unsigned m_next_visibility_bit = 0U;
+
+public:
+ view_environment(layout_environment &next, char const *name)
+ : layout_environment(next)
+ , m_name(name)
+ {
+ }
+ view_environment(view_environment &next, bool visibility)
+ : layout_environment(next)
+ , m_next_view(&next)
+ , m_name(next.m_name)
+ , m_visibility_mask(next.m_visibility_mask | (u32(visibility ? 1 : 0) << next.m_next_visibility_bit))
+ , m_next_visibility_bit(next.m_next_visibility_bit + (visibility ? 1 : 0))
+ {
+ if (32U < m_next_visibility_bit)
+ throw layout_syntax_error(util::string_format("view '%s' contains too many visibility toggles", m_name));
+ }
+ ~view_environment()
+ {
+ if (m_next_view)
+ m_next_view->m_next_visibility_bit = m_next_visibility_bit;
+ }
+
+ u32 visibility_mask() const { return m_visibility_mask; }
+};
+
+} // namespace emu::render::detail
+namespace {
-//**************************************************************************
-// GLOBAL VARIABLES
-//**************************************************************************
+bool add_bounds_step(emu::render::detail::layout_environment &env, emu::render::detail::bounds_vector &steps, util::xml::data_node const &node)
+{
+ int const state(env.get_attribute_int(node, "state", 0));
+ auto const pos(
+ std::lower_bound(
+ steps.begin(),
+ steps.end(),
+ state,
+ [] (emu::render::detail::bounds_step const &lhs, int rhs) { return lhs.state < rhs; }));
+ if ((steps.end() != pos) && (state == pos->state))
+ return false;
+
+ auto &ins(*steps.emplace(pos, emu::render::detail::bounds_step{ state, { 0.0F, 0.0F, 0.0F, 0.0F }, { 0.0F, 0.0F, 0.0F, 0.0F } }));
+ env.parse_bounds(&node, ins.bounds);
+ return true;
+}
+
+void set_bounds_deltas(emu::render::detail::bounds_vector &steps)
+{
+ if (steps.empty())
+ {
+ steps.emplace_back(emu::render::detail::bounds_step{ 0, { 0.0F, 0.0F, 1.0F, 1.0F }, { 0.0F, 0.0F, 0.0F, 0.0F } });
+ }
+ else
+ {
+ auto i(steps.begin());
+ auto j(i);
+ while (steps.end() != ++j)
+ {
+ assert(j->state > i->state);
+
+ i->delta.x0 = (j->bounds.x0 - i->bounds.x0) / (j->state - i->state);
+ i->delta.x1 = (j->bounds.x1 - i->bounds.x1) / (j->state - i->state);
+ i->delta.y0 = (j->bounds.y0 - i->bounds.y0) / (j->state - i->state);
+ i->delta.y1 = (j->bounds.y1 - i->bounds.y1) / (j->state - i->state);
+
+ i = j;
+ }
+ }
+}
+
+void normalize_bounds(emu::render::detail::bounds_vector &steps, float x0, float y0, float xoffs, float yoffs, float xscale, float yscale)
+{
+ auto i(steps.begin());
+ i->bounds.x0 = x0 + (i->bounds.x0 - xoffs) * xscale;
+ i->bounds.x1 = x0 + (i->bounds.x1 - xoffs) * xscale;
+ i->bounds.y0 = y0 + (i->bounds.y0 - yoffs) * yscale;
+ i->bounds.y1 = y0 + (i->bounds.y1 - yoffs) * yscale;
+
+ auto j(i);
+ while (steps.end() != ++j)
+ {
+ j->bounds.x0 = x0 + (j->bounds.x0 - xoffs) * xscale;
+ j->bounds.x1 = x0 + (j->bounds.x1 - xoffs) * xscale;
+ j->bounds.y0 = y0 + (j->bounds.y0 - yoffs) * yscale;
+ j->bounds.y1 = y0 + (j->bounds.y1 - yoffs) * yscale;
+
+ i->delta.x0 = (j->bounds.x0 - i->bounds.x0) / (j->state - i->state);
+ i->delta.x1 = (j->bounds.x1 - i->bounds.x1) / (j->state - i->state);
+ i->delta.y0 = (j->bounds.y0 - i->bounds.y0) / (j->state - i->state);
+ i->delta.y1 = (j->bounds.y1 - i->bounds.y1) / (j->state - i->state);
+
+ i = j;
+ }
+}
+
+render_bounds accumulate_bounds(emu::render::detail::bounds_vector const &steps)
+{
+ auto i(steps.begin());
+ render_bounds result(i->bounds);
+ while (steps.end() != ++i)
+ result |= i->bounds;
+ return result;
+}
+
+inline render_bounds interpolate_bounds(emu::render::detail::bounds_vector const &steps, int state)
+{
+ auto pos(
+ std::lower_bound(
+ steps.begin(),
+ steps.end(),
+ state,
+ [] (emu::render::detail::bounds_step const &lhs, int rhs) { return lhs.state < rhs; }));
+ if (steps.begin() == pos)
+ {
+ return pos->bounds;
+ }
+ else
+ {
+ --pos;
+ render_bounds result(pos->bounds);
+ result.x0 += pos->delta.x0 * (state - pos->state);
+ result.x1 += pos->delta.x1 * (state - pos->state);
+ result.y0 += pos->delta.y0 * (state - pos->state);
+ result.y1 += pos->delta.y1 * (state - pos->state);
+ return result;
+ }
+}
+
+
+bool add_color_step(emu::render::detail::layout_environment &env, emu::render::detail::color_vector &steps, util::xml::data_node const &node)
+{
+ int const state(env.get_attribute_int(node, "state", 0));
+ auto const pos(
+ std::lower_bound(
+ steps.begin(),
+ steps.end(),
+ state,
+ [] (emu::render::detail::color_step const &lhs, int rhs) { return lhs.state < rhs; }));
+ if ((steps.end() != pos) && (state == pos->state))
+ return false;
+
+ steps.emplace(pos, emu::render::detail::color_step{ state, env.parse_color(&node), { 0.0F, 0.0F, 0.0F, 0.0F } });
+ return true;
+}
+
+void set_color_deltas(emu::render::detail::color_vector &steps)
+{
+ if (steps.empty())
+ {
+ steps.emplace_back(emu::render::detail::color_step{ 0, { 1.0F, 1.0F, 1.0F, 1.0F }, { 0.0F, 0.0F, 0.0F, 0.0F } });
+ }
+ else
+ {
+ auto i(steps.begin());
+ auto j(i);
+ while (steps.end() != ++j)
+ {
+ assert(j->state > i->state);
+
+ i->delta.a = (j->color.a - i->color.a) / (j->state - i->state);
+ i->delta.r = (j->color.r - i->color.r) / (j->state - i->state);
+ i->delta.g = (j->color.g - i->color.g) / (j->state - i->state);
+ i->delta.b = (j->color.b - i->color.b) / (j->state - i->state);
+
+ i = j;
+ }
+ }
+}
+
+inline render_color interpolate_color(emu::render::detail::color_vector const &steps, int state)
+{
+ auto pos(
+ std::lower_bound(
+ steps.begin(),
+ steps.end(),
+ state,
+ [] (emu::render::detail::color_step const &lhs, int rhs) { return lhs.state < rhs; }));
+ if (steps.begin() == pos)
+ {
+ return pos->color;
+ }
+ else
+ {
+ --pos;
+ render_color result(pos->color);
+ result.a += pos->delta.a * (state - pos->state);
+ result.r += pos->delta.r * (state - pos->state);
+ result.g += pos->delta.g * (state - pos->state);
+ result.b += pos->delta.b * (state - pos->state);
+ return result;
+ }
+}
+
+
+unsigned get_state_shift(ioport_value mask)
+{
+ // get shift to right-align LSB
+ unsigned result(0U);
+ while (mask && !BIT(mask, 0))
+ {
+ ++result;
+ mask >>= 1;
+ }
+ return result;
+}
+
+std::string make_child_output_tag(
+ emu::render::detail::view_environment &env,
+ util::xml::data_node const &itemnode,
+ char const *child)
+{
+ util::xml::data_node const *const childnode(itemnode.get_child(child));
+ if (childnode)
+ return std::string(env.get_attribute_string(*childnode, "name"));
+ else
+ return std::string();
+}
+
+std::string make_child_input_tag(
+ emu::render::detail::view_environment &env,
+ util::xml::data_node const &itemnode,
+ char const *child)
+{
+ util::xml::data_node const *const childnode(itemnode.get_child(child));
+ return childnode ? env.get_attribute_subtag(*childnode, "inputtag") : std::string();
+}
+
+ioport_value make_child_mask(
+ emu::render::detail::view_environment &env,
+ util::xml::data_node const &itemnode,
+ char const *child)
+{
+ util::xml::data_node const *const childnode(itemnode.get_child(child));
+ return childnode ? env.get_attribute_int(*childnode, "mask", ~ioport_value(0)) : ~ioport_value(0);
+}
+
+bool make_child_wrap(
+ emu::render::detail::view_environment &env,
+ util::xml::data_node const &itemnode,
+ char const *child)
+{
+ util::xml::data_node const *const childnode(itemnode.get_child(child));
+ return childnode ? env.get_attribute_bool(*childnode, "wrap", false) : false;
+}
+
+float make_child_size(
+ emu::render::detail::view_environment &env,
+ util::xml::data_node const &itemnode,
+ char const *child)
+{
+ util::xml::data_node const *const childnode(itemnode.get_child(child));
+ return std::clamp(childnode ? env.get_attribute_float(*childnode, "size", 1.0f) : 1.0f, 0.01f, 1.0f);
+}
+
+ioport_value make_child_min(
+ emu::render::detail::view_environment &env,
+ util::xml::data_node const &itemnode,
+ char const *child)
+{
+ util::xml::data_node const *const childnode(itemnode.get_child(child));
+ return childnode ? env.get_attribute_int(*childnode, "min", ioport_value(0)) : ioport_value(0);
+}
+
+ioport_value make_child_max(
+ emu::render::detail::view_environment &env,
+ util::xml::data_node const &itemnode,
+ char const *child,
+ ioport_value mask)
+{
+ util::xml::data_node const *const childnode(itemnode.get_child(child));
+ ioport_value const dflt(mask >> get_state_shift(mask));
+ return childnode ? env.get_attribute_int(*childnode, "max", dflt) : dflt;
+}
+
+std::string make_input_tag(
+ emu::render::detail::view_environment &env,
+ util::xml::data_node const &itemnode)
+{
+ return env.get_attribute_subtag(itemnode, "inputtag");
+}
+
+int get_blend_mode(emu::render::detail::view_environment &env, util::xml::data_node const &itemnode)
+{
+ // see if there's a blend mode attribute
+ std::string const *const mode(itemnode.get_attribute_string_ptr("blend"));
+ if (mode)
+ {
+ if (*mode == "none")
+ return BLENDMODE_NONE;
+ else if (*mode == "alpha")
+ return BLENDMODE_ALPHA;
+ else if (*mode == "multiply")
+ return BLENDMODE_RGB_MULTIPLY;
+ else if (*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 -1; // magic number recognised by render.cpp to allow per-element blend mode
+ else if (!strcmp(itemnode.get_name(), "overlay"))
+ return BLENDMODE_RGB_MULTIPLY;
+ else
+ return BLENDMODE_ALPHA;
+}
-render_screen_list render_target::s_empty_screen_list;
+} // anonymous namespace
@@ -855,13 +1257,9 @@ render_screen_list render_target::s_empty_screen_list;
layout_element::make_component_map const layout_element::s_make_component{
{ "image", &make_component<image_component> },
{ "text", &make_component<text_component> },
- { "dotmatrix", &make_dotmatrix_component<8> },
- { "dotmatrix5dot", &make_dotmatrix_component<5> },
- { "dotmatrixdot", &make_dotmatrix_component<1> },
{ "simplecounter", &make_component<simplecounter_component> },
{ "reel", &make_component<reel_component> },
{ "led7seg", &make_component<led7seg_component> },
- { "led8seg_gts1", &make_component<led8seg_gts1_component> },
{ "led14seg", &make_component<led14seg_component> },
{ "led14segsc", &make_component<led14segsc_component> },
{ "led16seg", &make_component<led16seg_component> },
@@ -874,14 +1272,13 @@ layout_element::make_component_map const layout_element::s_make_component{
// layout_element - constructor
//-------------------------------------------------
-layout_element::layout_element(environment &env, util::xml::data_node const &elemnode, const char *dirname)
+layout_element::layout_element(environment &env, util::xml::data_node const &elemnode)
: m_machine(env.machine())
- , m_defstate(0)
- , m_maxstate(0)
+ , m_defstate(env.get_attribute_int(elemnode, "defstate", -1))
+ , m_statemask(0)
+ , m_foldhigh(false)
+ , m_invalidated(false)
{
- // get the default state
- m_defstate = env.get_attribute_int(elemnode, "defstate", -1);
-
// parse components in order
bool first = true;
render_bounds bounds = { 0.0, 0.0, 0.0, 0.0 };
@@ -892,17 +1289,19 @@ layout_element::layout_element(environment &env, util::xml::data_node const &ele
throw layout_syntax_error(util::string_format("unknown element component %s", compnode->get_name()));
// insert the new component into the list
- component const &newcomp(**m_complist.emplace(m_complist.end(), make_func->second(env, *compnode, dirname)));
+ component const &newcomp(*m_complist.emplace_back(make_func->second(env, *compnode)));
// accumulate bounds
if (first)
- bounds = newcomp.bounds();
+ bounds = newcomp.overall_bounds();
else
- union_render_bounds(bounds, newcomp.bounds());
+ bounds |= newcomp.overall_bounds();
first = false;
// determine the maximum state
- m_maxstate = std::max(m_maxstate, newcomp.maxstate());
+ std::pair<int, bool> const wrap(newcomp.statewrap());
+ m_statemask |= wrap.first;
+ m_foldhigh = m_foldhigh || wrap.second;
}
if (!m_complist.empty())
@@ -910,8 +1309,8 @@ layout_element::layout_element(environment &env, util::xml::data_node const &ele
// determine the scale/offset for normalization
float xoffs = bounds.x0;
float yoffs = bounds.y0;
- float xscale = 1.0f / (bounds.x1 - bounds.x0);
- float yscale = 1.0f / (bounds.y1 - bounds.y0);
+ float xscale = 1.0F / (bounds.x1 - bounds.x0);
+ float yscale = 1.0F / (bounds.y1 - bounds.y0);
// normalize all the component bounds
for (component::ptr const &curcomp : m_complist)
@@ -919,7 +1318,7 @@ layout_element::layout_element(environment &env, util::xml::data_node const &ele
}
// allocate an array of element textures for the states
- m_elemtex.resize(m_maxstate + 1);
+ m_elemtex.resize((m_statemask + 1) << (m_foldhigh ? 1 : 0));
}
@@ -943,7 +1342,7 @@ layout_element::~layout_element()
layout_group::layout_group(util::xml::data_node const &groupnode)
: m_groupnode(groupnode)
- , m_bounds{ 0.0f, 0.0f, 0.0f, 0.0f }
+ , m_bounds{ 0.0F, 0.0F, 0.0F, 0.0F }
, m_bounds_resolved(false)
{
}
@@ -1051,17 +1450,18 @@ void layout_group::resolve_bounds(environment &env, group_map &groupmap, std::ve
// a wild loop appears!
std::ostringstream path;
for (layout_group const *const group : seen)
- path << ' ' << group->m_groupnode.get_attribute_string("name", nullptr);
- path << ' ' << m_groupnode.get_attribute_string("name", nullptr);
+ path << ' ' << group->m_groupnode.get_attribute_string("name", "");
+ path << ' ' << m_groupnode.get_attribute_string("name", "");
throw layout_syntax_error(util::string_format("recursively nested groups %s", path.str()));
}
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);
- resolve_bounds(local, m_groupnode, groupmap, seen, true, false, true);
+ bool empty(true);
+ resolve_bounds(local, m_groupnode, groupmap, seen, empty, false, false, true);
}
seen.pop_back();
}
@@ -1071,10 +1471,13 @@ void layout_group::resolve_bounds(
util::xml::data_node const &parentnode,
group_map &groupmap,
std::vector<layout_group const *> &seen,
- bool empty,
+ bool &empty,
+ bool vistoggle,
bool repeat,
bool init)
{
+ LOGMASKED(LOG_GROUP_BOUNDS_RESOLUTION, "Group '%s' resolve bounds empty=%s vistoggle=%s repeat=%s init=%s\n",
+ parentnode.get_attribute_string("name", ""), empty, vistoggle, repeat, init);
bool envaltered(false);
bool unresolved(true);
for (util::xml::data_node const *itemnode = parentnode.get_first_child(); !m_bounds_resolved && itemnode; itemnode = itemnode->get_next_sibling())
@@ -1090,6 +1493,7 @@ void layout_group::resolve_bounds(
envaltered = true;
if (!unresolved)
{
+ LOGMASKED(LOG_GROUP_BOUNDS_RESOLUTION, "Environment altered%s, unresolving groups\n", envaltered ? " again" : "");
unresolved = true;
for (group_map::value_type &group : groupmap)
group.second.set_bounds_unresolved();
@@ -1108,12 +1512,26 @@ void layout_group::resolve_bounds(
!strcmp(itemnode->get_name(), "marquee"))
{
render_bounds itembounds;
- env.parse_bounds(itemnode->get_child("bounds"), itembounds);
+ util::xml::data_node const *boundsnode = itemnode->get_child("bounds");
+ env.parse_bounds(boundsnode, itembounds);
+ while (boundsnode)
+ {
+ boundsnode = boundsnode->get_next_sibling("bounds");
+ if (boundsnode)
+ {
+ render_bounds b;
+ env.parse_bounds(boundsnode, 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,
+ m_bounds.x0, m_bounds.y0, m_bounds.x1, m_bounds.y1);
}
else if (!strcmp(itemnode->get_name(), "group"))
{
@@ -1125,14 +1543,18 @@ 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", ""),
+ itembounds.x0, itembounds.y0, itembounds.x1, itembounds.y1,
+ m_bounds.x0, m_bounds.y0, m_bounds.x1, m_bounds.y1);
}
else
{
- char const *ref(env.get_attribute_string(*itemnode, "ref", nullptr));
- if (!ref)
- throw layout_syntax_error("nested group must have ref attribute");
+ std::string const ref(env.get_attribute_string(*itemnode, "ref"));
+ if (ref.empty())
+ throw layout_syntax_error("nested group must have non-empty ref attribute");
group_map::iterator const found(groupmap.find(ref));
if (groupmap.end() == found)
@@ -1149,8 +1571,13 @@ 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",
+ itemnode->get_attribute_string("ref", ""),
+ itembounds.x0, itembounds.y0, itembounds.x1, itembounds.y1,
+ m_bounds.x0, m_bounds.y0, m_bounds.x1, m_bounds.y1);
}
}
else if (!strcmp(itemnode->get_name(), "repeat"))
@@ -1161,10 +1588,17 @@ void layout_group::resolve_bounds(
environment local(env);
for (int i = 0; !m_bounds_resolved && (count > i); ++i)
{
- resolve_bounds(local, *itemnode, groupmap, seen, empty, true, !i);
+ resolve_bounds(local, *itemnode, groupmap, seen, empty, false, true, !i);
local.increment_parameters();
}
}
+ else if (!strcmp(itemnode->get_name(), "collection"))
+ {
+ if (!itemnode->has_attribute("name"))
+ throw layout_syntax_error("collection must have name attribute");
+ environment local(env);
+ resolve_bounds(local, *itemnode, groupmap, seen, empty, true, false, true);
+ }
else
{
throw layout_syntax_error(util::string_format("unknown group element %s", itemnode->get_name()));
@@ -1173,14 +1607,19 @@ void layout_group::resolve_bounds(
if (envaltered && !unresolved)
{
+ LOGMASKED(LOG_GROUP_BOUNDS_RESOLUTION, "Environment was altered, marking groups unresolved\n");
bool const resolved(m_bounds_resolved);
for (group_map::value_type &group : groupmap)
group.second.set_bounds_unresolved();
m_bounds_resolved = resolved;
}
- if (!repeat)
+ if (!vistoggle && !repeat)
+ {
+ LOGMASKED(LOG_GROUP_BOUNDS_RESOLUTION, "Marking group '%s' bounds resolved\n",
+ parentnode.get_attribute_string("name", ""));
m_bounds_resolved = true;
+ }
}
@@ -1193,8 +1632,12 @@ void layout_group::resolve_bounds(
render_texture *layout_element::state_texture(int state)
{
- assert(state <= m_maxstate);
- if (m_elemtex[state].m_texture == nullptr)
+ if (m_foldhigh && (state & ~m_statemask))
+ state = (state & m_statemask) | (((m_statemask << 1) | 1) & ~m_statemask);
+ else
+ state &= m_statemask;
+ assert(m_elemtex.size() > state);
+ if (!m_elemtex[state].m_texture)
{
m_elemtex[state].m_element = this;
m_elemtex[state].m_state = state;
@@ -1205,6 +1648,48 @@ render_texture *layout_element::state_texture(int state)
//-------------------------------------------------
+// set_draw_callback - set handler called after
+// drawing components
+//-------------------------------------------------
+
+void layout_element::set_draw_callback(draw_delegate &&handler)
+{
+ m_draw = std::move(handler);
+}
+
+
+//-------------------------------------------------
+// preload - perform expensive loading upfront
+// for all components
+//-------------------------------------------------
+
+void layout_element::preload()
+{
+ for (component::ptr const &curcomp : m_complist)
+ curcomp->preload(machine());
+}
+
+
+//-------------------------------------------------
+// prepare - perform additional tasks before
+// drawing a frame
+//-------------------------------------------------
+
+void layout_element::prepare()
+{
+ if (m_invalidated)
+ {
+ m_invalidated = false;
+ for (texture &tex : m_elemtex)
+ {
+ machine().render().texture_free(tex.m_texture);
+ tex.m_texture = nullptr;
+ }
+ }
+}
+
+
+//-------------------------------------------------
// element_scale - scale an element by rendering
// all the components at the appropriate
// resolution
@@ -1212,23 +1697,18 @@ render_texture *layout_element::state_texture(int state)
void layout_element::element_scale(bitmap_argb32 &dest, bitmap_argb32 &source, const rectangle &sbounds, void *param)
{
- texture *elemtex = (texture *)param;
+ texture const &elemtex(*reinterpret_cast<texture const *>(param));
- // iterate over components that are part of the current state
- for (auto &curcomp : elemtex->m_element->m_complist)
- if (curcomp->state() == -1 || curcomp->state() == elemtex->m_state)
- {
- // get the local scaled bounds
- rectangle bounds(
- render_round_nearest(curcomp->bounds().x0 * dest.width()),
- render_round_nearest(curcomp->bounds().x1 * dest.width()),
- render_round_nearest(curcomp->bounds().y0 * dest.height()),
- render_round_nearest(curcomp->bounds().y1 * dest.height()));
- bounds &= dest.cliprect();
+ // draw components that are visible in the current state
+ for (auto const &curcomp : elemtex.m_element->m_complist)
+ {
+ if ((elemtex.m_state & curcomp->statemask()) == curcomp->stateval())
+ curcomp->draw(elemtex.m_element->machine(), dest, elemtex.m_state);
+ }
- // based on the component type, add to the texture
- curcomp->draw(elemtex->m_element->machine(), dest, bounds, elemtex->m_state);
- }
+ // if there's a callback for additional drawing, invoke it
+ if (!elemtex.m_element->m_draw.isnull())
+ elemtex.m_element->m_draw(elemtex.m_state, dest);
}
@@ -1237,80 +1717,410 @@ class layout_element::image_component : public component
{
public:
// construction/destruction
- image_component(environment &env, util::xml::data_node const &compnode, const char *dirname)
- : component(env, compnode, dirname)
- , m_hasalpha(false)
+ image_component(environment &env, util::xml::data_node const &compnode)
+ : component(env, compnode)
+ , m_rasterizer(env.svg_rasterizer())
+ , m_searchpath(env.search_path() ? env.search_path() : "")
+ , m_dirname(env.directory_name() ? env.directory_name() : "")
+ , m_imagefile(env.get_attribute_string(compnode, "file"))
+ , m_alphafile(env.get_attribute_string(compnode, "alphafile"))
+ , m_data(get_data(compnode))
{
- if (dirname != nullptr)
- m_dirname = dirname;
- m_imagefile = env.get_attribute_string(compnode, "file", "");
- m_alphafile = env.get_attribute_string(compnode, "alphafile", "");
- m_file = std::make_unique<emu_file>(env.machine().options().art_path(), OPEN_FLAG_READ);
}
-protected:
// overrides
- virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
+ virtual void preload(running_machine &machine) override
+ {
+ if (!m_bitmap.valid() && !m_svg)
+ load_image(machine);
+ }
+
+protected:
+ virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, rectangle const &bounds, int state) override
{
- if (!m_bitmap.valid())
- load_bitmap();
+ if (!m_bitmap.valid() && !m_svg)
+ load_image(machine);
- bitmap_argb32 destsub(dest, bounds);
- render_resample_argb_bitmap_hq(destsub, m_bitmap, color());
+ if (m_bitmap.valid())
+ draw_bitmap(dest, bounds, state);
+ else if (m_svg)
+ draw_svg(dest, bounds, state);
}
private:
// internal helpers
- void load_bitmap()
+ void draw_bitmap(bitmap_argb32 &dest, rectangle const &bounds, int state)
{
- assert(m_file != nullptr);
+ 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);
+ alpha_blend(tempbitmap, dest, bounds);
+ }
+ else
+ {
+ bitmap_argb32 destsub(dest, bounds);
+ render_resample_argb_bitmap_hq(destsub, m_bitmap, c);
+ }
+ }
- ru_imgformat const format = render_detect_image(*m_file, m_dirname.c_str(), m_imagefile.c_str());
- switch (format)
+ 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)
{
- case RENDUTIL_IMGFORMAT_ERROR:
- break;
+ 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.5F),
+ u8((float(src[0]) * c.r) + 0.5F),
+ u8((float(src[1]) * c.g) + 0.5F),
+ u8((float(src[2]) * c.b) + 0.5F));
+ *dst = d;
+ havealpha = havealpha || (d.a() < 255U);
+ }
+ }
- case RENDUTIL_IMGFORMAT_PNG:
- // load the basic bitmap
- m_hasalpha = render_load_png(m_bitmap, *m_file, m_dirname.c_str(), m_imagefile.c_str());
- break;
+ // 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
+ {
+ 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);
+ }
+ }
- default:
- // try JPG
- render_load_jpeg(m_bitmap, *m_file, m_dirname.c_str(), m_imagefile.c_str());
- break;
+ 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 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;
+ }
+ }
+ }
+ }
+ }
+
+ void load_image(running_machine &machine)
+ {
+ // if we have a filename, go with that
+ emu_file file(m_searchpath.empty() ? m_dirname : m_searchpath, OPEN_FLAG_READ);
+ if (!m_imagefile.empty())
+ {
+ std::string filename;
+ if (!m_searchpath.empty())
+ filename = m_dirname;
+ util::path_append(filename, m_imagefile);
+ LOGMASKED(LOG_IMAGE_LOAD, "Image component attempt to load image file '%s'\n", filename);
+ std::error_condition const imgerr = file.open(filename);
+ if (!imgerr)
+ {
+ if (!load_bitmap(file))
+ {
+ LOGMASKED(LOG_IMAGE_LOAD, "Image component will attempt to parse file as SVG\n");
+ load_svg(file);
+ }
+ file.close();
+ }
+ else
+ {
+ LOGMASKED(LOG_IMAGE_LOAD, "Image component unable to open image file '%s' (%s:%d %s)\n",
+ filename, imgerr.category().name(), imgerr.value(), imgerr.message());
+ }
+ }
+ else if (!m_data.empty())
+ {
+ load_image_data();
}
// load the alpha bitmap if specified
- if (m_bitmap.valid() && !m_alphafile.empty())
- render_load_png(m_bitmap, *m_file, m_dirname.c_str(), m_alphafile.c_str(), true);
+ if (!m_alphafile.empty())
+ {
+ if (m_bitmap.valid())
+ {
+ std::string filename;
+ if (!m_searchpath.empty())
+ filename = m_dirname;
+ util::path_append(filename, m_alphafile);
+ LOGMASKED(LOG_IMAGE_LOAD, "Image component attempt to load alpha channel from file '%s'\n", filename);
+ std::error_condition const alferr = file.open(filename);
+ if (!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:%d %s)\n",
+ filename, alferr.category().name(), alferr.value(), alferr.message());
+ }
+ }
+ 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);
m_bitmap.fill(0);
for (int step = 0; step < 100; step += 25)
for (int line = 0; line < 100; line++)
- m_bitmap.pix32((step + line) % 100, line % 100) = rgb_t(0xff,0xff,0xff,0xff);
+ m_bitmap.pix((step + line) % 100, line % 100) = rgb_t(0xff,0xff,0xff,0xff);
// log an error
if (m_alphafile.empty())
- osd_printf_warning("Unable to load component bitmap '%s'\n", m_imagefile.c_str());
+ 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.c_str(), m_alphafile.c_str());
+ 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_searchpath.clear();
+ m_dirname.clear();
+ m_imagefile.clear();
+ m_alphafile.clear();
+ m_data.clear();
+ }
+
+ void load_image_data()
+ {
+ // in-place Base64 decode
+ static constexpr char base64chars[] =
+ "\t\n\v\f\r +/0123456789"
+ "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz";
+ static constexpr char base64tail[] =
+ "\t\n\v\f\r =";
+ std::string::size_type const tail(m_data.find_first_not_of(base64chars));
+ std::string::size_type const end(m_data.find_first_not_of(base64tail, tail));
+ if (std::string::npos == end)
+ {
+ LOGMASKED(LOG_IMAGE_LOAD, "Image component decoding Base64 image data\n");
+ char *dst(&m_data[0]);
+ unsigned trailing(0U);
+ for (std::string::size_type i = 0U; (m_data.size() > i) && ('=' != m_data[i]); ++i)
+ {
+ u8 sym;
+ if (('A' <= m_data[i]) && ('Z' >= m_data[i]))
+ sym = m_data[i] - 'A';
+ else if (('a' <= m_data[i]) && ('z' >= m_data[i]))
+ sym = m_data[i] - 'a' + 26;
+ else if (('0' <= m_data[i]) && ('9' >= m_data[i]))
+ sym = m_data[i] - '0' + 52;
+ else if ('+' == m_data[i])
+ sym = 62;
+ else if ('/' == m_data[i])
+ sym = 63;
+ else
+ continue;
+ if (trailing)
+ *dst |= (sym << 2) >> trailing;
+ else
+ *dst = sym << 2;
+ if (trailing >= 2U)
+ ++dst;
+ trailing = (trailing + 6U) & 7U;
+ if (trailing)
+ *dst = sym << (8U - trailing);
+ }
+ m_data.resize(dst - &m_data[0]);
+ }
+
+ // make a file wrapper for the data and see if it looks like a bitmap
+ util::core_file::ptr file;
+ std::error_condition const filerr(util::core_file::open_ram(m_data.c_str(), m_data.size(), OPEN_FLAG_READ, file));
+ bool const bitmapdata(!filerr && file && load_bitmap(*file));
+ file.reset();
+
+ // if it didn't look like a bitmap, see if it looks like it might be XML and hence SVG
+ if (!bitmapdata)
+ {
+ bool const utf16be((0xfe == u8(m_data[0])) && (0xff == u8(m_data[1])));
+ bool const utf16le((0xff == u8(m_data[0])) && (0xfe == u8(m_data[1])));
+ bool const utf8((0xef == u8(m_data[0])) && (0xbb == u8(m_data[1])) && (0xbf == u8(m_data[2])));
+ std::string::size_type const found(m_data.find_first_not_of("\t\n\v\f\r "));
+ bool const xmltag((std::string::npos != found) && ('<' == m_data[found]));
+ if (utf16be || utf16le || utf8 || xmltag)
+ {
+ LOGMASKED(LOG_IMAGE_LOAD, "Image component will attempt to parse data as SVG\n");
+ parse_svg(&m_data[0]);
+ }
+ }
+ }
+
+ bool load_bitmap(util::random_read &file)
+ {
+ 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");
+ return false;
+
+ case RENDUTIL_IMGFORMAT_PNG:
+ LOGMASKED(LOG_IMAGE_LOAD, "Image component detected PNG file format\n");
+ m_hasalpha = render_load_png(m_bitmap, file);
+ return true;
+
+ case RENDUTIL_IMGFORMAT_JPEG:
+ LOGMASKED(LOG_IMAGE_LOAD, "Image component detected JPEG file format\n");
+ render_load_jpeg(m_bitmap, file);
+ return true;
+
+ case RENDUTIL_IMGFORMAT_MSDIB:
+ LOGMASKED(LOG_IMAGE_LOAD, "Image component detected Microsoft DIB file format\n");
+ render_load_msdib(m_bitmap, file);
+ return true;
+
+ default:
+ LOGMASKED(LOG_IMAGE_LOAD, "Image component failed to detect bitmap file format\n");
+ return false;
+ }
+ }
+
+ void load_svg(util::random_read &file)
+ {
+ std::error_condition filerr;
+ u64 len;
+ filerr = file.length(len);
+ if (filerr)
+ {
+ osd_printf_warning("Error getting length of component image '%s'\n", m_imagefile);
+ return;
+ }
+ 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';
+ size_t actual;
+ std::tie(filerr, actual) = read(file, svgbuf.get(), len);
+ if (filerr || (actual < len))
+ {
+ osd_printf_warning("Error reading component image '%s'\n", m_imagefile);
+ return;
+ }
+ parse_svg(svgbuf.get());
+ }
+
+ void parse_svg(char *svgdata)
+ {
+ if (!m_rasterizer)
+ {
+ osd_printf_warning("No SVG rasteriser available, won't attempt to parse component image '%s' as SVG\n", m_imagefile);
+ return;
+ }
+ m_svg.reset(nsvgParse(svgdata, "px", 72));
+ 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 m_dirname; // directory name of image file (for lazy loading)
- std::unique_ptr<emu_file> m_file; // file object for reading image/alpha files
- std::string m_imagefile; // name of the image file (for lazy loading)
- std::string m_alphafile; // name of the alpha file (for lazy loading)
- bool m_hasalpha; // 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_searchpath; // asset search path (for lazy loading)
+ 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
};
@@ -1319,41 +2129,42 @@ class layout_element::rect_component : public component
{
public:
// construction/destruction
- rect_component(environment &env, util::xml::data_node const &compnode, const char *dirname)
- : component(env, compnode, dirname)
+ rect_component(environment &env, util::xml::data_node const &compnode)
+ : component(env, compnode)
{
}
protected:
// overrides
- virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
+ virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
{
- // compute premultiplied colors
- u32 const r = color().r * color().a * 255.0f;
- u32 const g = color().g * color().a * 255.0f;
- u32 const b = color().b * color().a * 255.0f;
- u32 const inva = (1.0f - color().a) * 255.0f;
-
- // iterate over X and Y
- for (u32 y = bounds.top(); y <= bounds.bottom(); y++)
+ render_color const c(color(state));
+ if (1.0f <= c.a)
+ {
+ // optimise opaque pixels
+ u32 const f(rgb_t(u8(c.r * 255), u8(c.g * 255), u8(c.b * 255)));
+ s32 const width(bounds.width());
+ for (u32 y = bounds.top(); y <= bounds.bottom(); ++y)
+ std::fill_n(&dest.pix(y, bounds.left()), width, f);
+ }
+ else
{
- for (u32 x = bounds.left(); x <= bounds.right(); x++)
+ // compute premultiplied color
+ u32 const a(c.a * 255.0F);
+ u32 const r(u32(c.r * (255.0F * 255.0F)) * a);
+ u32 const g(u32(c.g * (255.0F * 255.0F)) * a);
+ u32 const b(u32(c.b * (255.0F * 255.0F)) * a);
+ u32 const inva(255 - a);
+
+ if (!a)
+ return;
+
+ // we're translucent, add in the destination pixel contribution
+ for (u32 y = bounds.top(); y <= bounds.bottom(); ++y)
{
- u32 finalr = r;
- u32 finalg = g;
- u32 finalb = b;
-
- // if we're translucent, add in the destination pixel contribution
- if (inva > 0)
- {
- rgb_t dpix = dest.pix32(y, x);
- finalr += (dpix.r() * inva) >> 8;
- finalg += (dpix.g() * inva) >> 8;
- finalb += (dpix.b() * inva) >> 8;
- }
-
- // store the target pixel, dividing the RGBA values by the overall scale factor
- dest.pix32(y, x) = rgb_t(finalr, finalg, finalb);
+ u32 *dst(&dest.pix(y, bounds.left()));
+ for (u32 x = bounds.left(); x <= bounds.right(); ++x, ++dst)
+ alpha_blend(*dst, a, r, g, b, inva);
}
}
}
@@ -1365,59 +2176,293 @@ class layout_element::disk_component : public component
{
public:
// construction/destruction
- disk_component(environment &env, util::xml::data_node const &compnode, const char *dirname)
- : component(env, compnode, dirname)
+ disk_component(environment &env, util::xml::data_node const &compnode)
+ : component(env, compnode)
{
}
-protected:
// overrides
- virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
+ virtual void draw(running_machine &machine, bitmap_argb32 &dest, int state) override
{
- // compute premultiplied colors
- u32 const r = color().r * color().a * 255.0f;
- u32 const g = color().g * color().a * 255.0f;
- u32 const b = color().b * color().a * 255.0f;
- u32 const inva = (1.0f - color().a) * 255.0f;
-
- // find the center
- float const xcenter = float(bounds.xcenter());
- float const ycenter = float(bounds.ycenter());
- float const xradius = float(bounds.width()) * 0.5f;
- float const yradius = float(bounds.height()) * 0.5f;
- float const ooyradius2 = 1.0f / (yradius * yradius);
+ // compute premultiplied color
+ render_color const c(color(state));
+ u32 const f(rgb_t(u8(c.r * 255), u8(c.g * 255), u8(c.b * 255)));
+ u32 const a(c.a * 255.0F);
+ u32 const r(c.r * (255.0F * 255.0F) * a);
+ u32 const g(c.g * (255.0F * 255.0F) * a);
+ u32 const b(c.b * (255.0F * 255.0F) * a);
+ u32 const inva(255 - a);
+
+ if (!a)
+ return;
- // iterate over y
- for (u32 y = bounds.top(); y <= bounds.bottom(); y++)
+ // calculate the position and size
+ render_bounds const curbounds = bounds(state);
+ double const xcenter = (curbounds.x0 + curbounds.x1) * double(dest.width()) * 0.5;
+ double const ycenter = (curbounds.y0 + curbounds.y1) * double(dest.height()) * 0.5;
+ double const xradius = curbounds.width() * double(dest.width()) * 0.5;
+ double const yradius = curbounds.height() * double(dest.height()) * 0.5;
+ s32 const miny = s32(curbounds.y0 * double(dest.height()));
+ s32 const maxy = s32(std::ceil(curbounds.y1 * double(dest.height()))) - 1;
+ LOGMASKED(LOG_DISK_DRAW, "Draw disk: bounds (%s %s %s %s); (((x - %s) ** 2) / (%s ** 2) + ((y - %s) ** 2) / (%s ** 2)) = 1; rows [%s %s]\n",
+ curbounds.x0, curbounds.y0, curbounds.x1, curbounds.y1, xcenter, xradius, ycenter, yradius, miny, maxy);
+
+ if (miny == maxy)
+ {
+ // fits in a single row of pixels - integrate entire area of ellipse
+ double const scale = xradius * yradius * 0.5;
+ s32 const minx = s32(curbounds.x0 * double(dest.width()));
+ s32 const maxx = s32(std::ceil(curbounds.x1 * double(dest.width()))) - 1;
+ double x1 = (double(minx) - xcenter) / xradius;
+ u32 *dst = &dest.pix(miny, minx);
+ for (s32 x = minx; maxx >= x; ++x, ++dst)
+ {
+ double const x0 = x1;
+ x1 = (double(x + 1) - xcenter) / xradius;
+ double const val = integral((std::max)(x0, -1.0), (std::min)(x1, 1.0)) * scale;
+ alpha_blend(*dst, c, val);
+ }
+ }
+ else
{
- float ycoord = ycenter - ((float)y + 0.5f);
- float xval = xradius * sqrtf(1.0f - (ycoord * ycoord) * ooyradius2);
+ double const scale = xradius * yradius * 0.25;
+ double const ooyradius2 = 1.0 / (yradius * yradius);
+ auto const draw_edge_row =
+ [&dest, &c, &curbounds, xcenter, xradius, scale, ooyradius2] (s32 row, double ycoord, bool cross_axis)
+ {
+ double const xval = xradius * std::sqrt((std::max)(1.0 - (ycoord * ycoord) * ooyradius2, 0.0));
+ double const l = xcenter - xval;
+ double const r = xcenter + xval;
+ if (!cross_axis)
+ {
+ s32 minx = s32(l);
+ s32 maxx = s32(std::ceil(r)) - 1;
+ double x1 = double(minx) - xcenter;
+ u32 *dst = &dest.pix(row, minx);
+ for (s32 x = minx; maxx >= x; ++x, ++dst)
+ {
+ double const x0 = x1;
+ x1 = double(x + 1) - xcenter;
+ double val = integral((std::max)(x0, -xval) / xradius, (std::min)(x1, xval) / xradius) * scale;
+ val -= ((std::min)(double(x + 1), r) - (std::max)(double(x), l)) * ycoord;
+ alpha_blend(*dst, c, val);
+ }
+ }
+ else
+ {
+ s32 const minx = s32(curbounds.x0 * double(dest.width()));
+ s32 const maxx = s32(std::ceil(curbounds.x1 * double(dest.width()))) - 1;
+ double x1 = (double(minx) - xcenter) / xradius;
+ u32 *dst = &dest.pix(row, minx);
+ for (s32 x = minx; maxx >= x; ++x, ++dst)
+ {
+ double const x0 = x1;
+ x1 = (double(x + 1) - xcenter) / xradius;
+ double val = integral((std::max)(x0, -1.0), (std::min)(x1, 1.0));
+ if (double(x + 1) <= l)
+ val += integral((std::max)(x0, -1.0), x1);
+ else if (double(x) <= l)
+ val += integral((std::max)(x0, -1.0), -xval / xradius);
+ if (double(x) >= r)
+ val += integral(x0, (std::min)(x1, 1.0));
+ else if (double(x + 1) >= r)
+ val += integral(xval / xradius, (std::min)(x1, 1.0));
+ val *= scale;
+ val -= (std::max)(((std::min)(double(x + 1), r) - (std::max)(double(x), l)), 0.0) * ycoord;
+ alpha_blend(*dst, c, val);
+ }
+ }
+ };
+
+ // draw the top row - in a thin ellipse it may extend below the axis
+ draw_edge_row(miny, ycenter - double(miny + 1), double(miny + 1) > ycenter);
+
+ // draw rows above the axis
+ s32 y = miny + 1;
+ double ycoord1 = ycenter - double(y);
+ double xval1 = std::sqrt((std::max)(1.0 - (ycoord1 * ycoord1) * ooyradius2, 0.0));
+ double l1 = xcenter - (xval1 * xradius);
+ double r1 = xcenter + (xval1 * xradius);
+ for ( ; (maxy > y) && (double(y + 1) <= ycenter); ++y)
+ {
+ double const xval0 = xval1;
+ double const l0 = l1;
+ double const r0 = r1;
+ ycoord1 = ycenter - double(y + 1);
+ xval1 = std::sqrt((std::max)(1.0 - (ycoord1 * ycoord1) * ooyradius2, 0.0));
+ l1 = xcenter - (xval1 * xradius);
+ r1 = xcenter + (xval1 * xradius);
+ s32 const minx = s32(l1);
+ s32 const maxx = s32(std::ceil(r1)) - 1;
+ s32 const minfill = s32(std::ceil(l0));
+ s32 const maxfill = s32(r0) - 1;
+ u32 *dst = &dest.pix(y, minx);
+ for (s32 x = minx; maxx >= x; ++x, ++dst)
+ {
+ if ((x >= minfill) && (x <= maxfill))
+ {
+ if (255 <= a)
+ dst = std::fill_n(dst, maxfill - x + 1, f);
+ else
+ while (x++ <= maxfill)
+ alpha_blend(*dst++, a, r, g, b, inva);
- // compute left/right coordinates
- s32 left = s32(xcenter - xval + 0.5f);
- s32 right = s32(xcenter + xval + 0.5f);
+ --dst;
+ x = maxfill;
+ }
+ else
+ {
+ double val = 0.0;
+
+ // integrate where perimeter passes through pixel cell
+ if (double(x + 1) <= l0) // perimeter intercepts right edge of pixel cell (left side)
+ val += integral((std::max)((double(x) - xcenter) / xradius, -xval1), (double(x + 1) - xcenter) / xradius);
+ else if (double(x) <= l0) // perimeter intercepts top edge of pixel cell (left side)
+ val += integral((std::max)((double(x) - xcenter) / xradius, -xval1), -xval0);
+ else if (double(x) >= r0) // perimeter intercepts left edge of pixel cell (right side)
+ val += integral((double(x) - xcenter) / xradius, (std::min)((double(x + 1) - xcenter) / xradius, xval1));
+ else if (double(x + 1) >= r0) // perimeter intercepts top edge of pixel cell (right side)
+ val += integral(xval0, (std::min)((double(x + 1) - xcenter) / xradius, xval1));
+ val *= scale;
+
+ // subtract area between vertical centre and bottom of pixel cell
+ if (double(x) <= l0)
+ val -= ((std::min)(double(x + 1), l0) - (std::max)(double(x), l1)) * ycoord1;
+ else if (double(x + 1) >= r0)
+ val -= ((std::min)(double(x + 1), r1) - (std::max)(double(x), r0)) * ycoord1;
+
+ // add in the fully covered part of the pixel
+ val += (std::max)((std::min)(double(x + 1), r0) - (std::max)(double(x), l0), 0.0);
+
+ alpha_blend(*dst, c, (std::min)(val, 1.0));
+ }
+ }
+ }
- // draw this scanline
- for (u32 x = left; x < right; x++)
+ // row spanning the axis
+ if ((maxy > y) && (double(y) < ycenter))
{
- u32 finalr = r;
- u32 finalg = g;
- u32 finalb = b;
-
- // if we're translucent, add in the destination pixel contribution
- if (inva > 0)
+ double const xval0 = xval1;
+ double const l0 = l1;
+ double const r0 = r1;
+ ycoord1 = double(y + 1) - ycenter;
+ xval1 = std::sqrt((std::max)(1.0 - (ycoord1 * ycoord1) * ooyradius2, 0.0));
+ l1 = xcenter - (xval1 * xradius);
+ r1 = xcenter + (xval1 * xradius);
+ s32 const minx = s32(curbounds.x0 * double(dest.width()));
+ s32 const maxx = s32(std::ceil(curbounds.x1 * double(dest.width()))) - 1;
+ u32 *dst = &dest.pix(y, minx);
+ for (s32 x = minx; maxx >= x; ++x, ++dst)
{
- rgb_t dpix = dest.pix32(y, x);
- finalr += (dpix.r() * inva) >> 8;
- finalg += (dpix.g() * inva) >> 8;
- finalb += (dpix.b() * inva) >> 8;
+ if ((double(x) >= (std::max)(l0, l1)) && (double(x + 1) <= (std::min)(r0, r1)))
+ {
+ if (255 <= a)
+ *dst = f;
+ else
+ alpha_blend(*dst, a, r, g, b, inva);
+ }
+ else
+ {
+ double val = 0.0;
+ if (double(x + 1) <= l0)
+ val += integral((xcenter - double(x + 1)) / xradius, (std::min)((xcenter - double(x)) / xradius, 1.0));
+ else if (double(x) <= l0)
+ val += integral(xval0, (std::min)((xcenter - double(x)) / xradius, 1.0));
+ else if (double(x) >= r0)
+ val += integral((double(x) - xcenter) / xradius, (std::min)((double(x + 1) - xcenter) / xradius, 1.0));
+ else if (double(x + 1) >= r0)
+ val += integral(xval0, (std::min)((double(x + 1) - xcenter) / xradius, 1.0));
+ if (double(x + 1) <= l1)
+ val += integral((xcenter - double(x + 1)) / xradius, (std::min)((xcenter - double(x)) / xradius, 1.0));
+ else if (double(x) <= l1)
+ val += integral(xval1, (std::min)((xcenter - double(x)) / xradius, 1.0));
+ else if (double(x) >= r1)
+ val += integral((double(x) - xcenter) / xradius, (std::min)((double(x + 1) - xcenter) / xradius, 1.0));
+ else if (double(x + 1) >= r1)
+ val += integral(xval1, (std::min)((double(x + 1) - xcenter) / xradius, 1.0));
+ val *= scale;
+ val += (std::max)(((std::min)(double(x + 1), r0) - (std::max)(double(x), l0)), 0.0) * (ycenter - double(y));
+ val += (std::max)(((std::min)(double(x + 1), r1) - (std::max)(double(x), l1)), 0.0) * (double(y + 1) - ycenter);
+ alpha_blend(*dst, c, val);
+ }
}
+ ++y;
+ }
+
+ // draw rows below the axis
+ for ( ; maxy > y; ++y)
+ {
+ double const ycoord0 = ycoord1;
+ double const xval0 = xval1;
+ double const l0 = l1;
+ double const r0 = r1;
+ ycoord1 = double(y + 1) - ycenter;
+ xval1 = std::sqrt((std::max)(1.0 - (ycoord1 * ycoord1) * ooyradius2, 0.0));
+ l1 = xcenter - (xval1 * xradius);
+ r1 = xcenter + (xval1 * xradius);
+ s32 const minx = s32(l0);
+ s32 const maxx = s32(std::ceil(r0)) - 1;
+ s32 const minfill = s32(std::ceil(l1));
+ s32 const maxfill = s32(r1) - 1;
+ u32 *dst = &dest.pix(y, minx);
+ for (s32 x = minx; maxx >= x; ++x, ++dst)
+ {
+ if ((x >= minfill) && (x <= maxfill))
+ {
+ if (255 <= a)
+ dst = std::fill_n(dst, maxfill - x + 1, f);
+ else
+ while (x++ <= maxfill)
+ alpha_blend(*dst++, a, r, g, b, inva);
- // store the target pixel, dividing the RGBA values by the overall scale factor
- dest.pix32(y, x) = rgb_t(finalr, finalg, finalb);
+ --dst;
+ x = maxfill;
+ }
+ else
+ {
+ double val = 0.0;
+
+ // integrate where perimeter passes through pixel cell
+ if (double(x + 1) <= l1) // perimeter intercepts right edge of pixel cell (left side)
+ val += integral((std::max)((double(x) - xcenter) / xradius, -xval0), (double(x + 1) - xcenter) / xradius);
+ else if (double(x) <= l1) // perimeter intercepts bottom edge of pixel cell (left side)
+ val += integral((std::max)((double(x) - xcenter) / xradius, -xval0), -xval1);
+ else if (double(x) >= r1) // perimeter intercepts left edge of pixel cell (right side)
+ val += integral((double(x) - xcenter) / xradius, (std::min)((double(x + 1) - xcenter) / xradius, xval0));
+ else if (double(x + 1) >= r1) // perimeter intercepts bottom edge of pixel cell (right side)
+ val += integral(xval1, (std::min)((double(x + 1) - xcenter) / xradius, xval0));
+ val *= scale;
+
+ // subtract area between vertical centre and top of pixel cell
+ if (double(x) <= l1)
+ val -= ((std::min)(double(x + 1), l1) - (std::max)(double(x), l0)) * ycoord0;
+ else if (double(x + 1) >= r1)
+ val -= ((std::min)(double(x + 1), r0) - (std::max)(double(x), r1)) * ycoord0;
+
+ // add in the fully covered part of the pixel
+ val += (std::max)((std::min)(double(x + 1), r1) - (std::max)(double(x), l1), 0.0);
+
+ alpha_blend(*dst, c, (std::min)(val, 1.0));
+ }
+ }
}
+
+ // last row is an inversion of the first
+ draw_edge_row(maxy, double(maxy) - ycenter, double(maxy) < ycenter);
}
}
+
+private:
+ static double integral(double x0, double x1)
+ {
+ return integral(x1) - integral(x0);
+ }
+
+ static double integral(double x)
+ {
+ double const u(2.0 * std::asin(x));
+ return u + std::sin(u);
+ };
};
@@ -1426,20 +2471,19 @@ class layout_element::text_component : public component
{
public:
// construction/destruction
- text_component(environment &env, util::xml::data_node const &compnode, const char *dirname)
- : component(env, compnode, dirname)
+ text_component(environment &env, util::xml::data_node const &compnode)
+ : component(env, compnode)
{
- m_string = env.get_attribute_string(compnode, "string", "");
+ m_string = env.get_attribute_string(compnode, "string");
m_textalign = env.get_attribute_int(compnode, "align", 0);
}
protected:
// overrides
- virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
+ virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
{
- render_font *font = machine.render().font_alloc("default");
- draw_text(*font, dest, bounds, m_string.c_str(), m_textalign);
- machine.render().font_free(font);
+ auto font = machine.render().font_alloc("default");
+ draw_text(*font, dest, bounds, m_string, m_textalign, color(state));
}
private:
@@ -1454,25 +2498,26 @@ class layout_element::led7seg_component : public component
{
public:
// construction/destruction
- led7seg_component(environment &env, util::xml::data_node const &compnode, const char *dirname)
- : component(env, compnode, dirname)
+ led7seg_component(environment &env, util::xml::data_node const &compnode)
+ : component(env, compnode)
{
+ m_invert = env.get_attribute_int(compnode, "invert", 0);
}
protected:
// overrides
virtual int maxstate() const override { return 255; }
- virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
+ virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
{
- const rgb_t onpen = rgb_t(0xff,0xff,0xff,0xff);
- const rgb_t offpen = rgb_t(0x20,0xff,0xff,0xff);
+ rgb_t const onpen = rgb_t(m_invert ? 0x20 : 0xff, 0xff, 0xff, 0xff);
+ rgb_t const offpen = rgb_t(m_invert ? 0xff : 0x20, 0xff, 0xff, 0xff);
// sizes for computation
- int bmwidth = 250;
- int bmheight = 400;
- int segwidth = 40;
- int skewwidth = 40;
+ int const bmwidth = 250;
+ int const bmheight = 400;
+ int const segwidth = 40;
+ int const skewwidth = 40;
// allocate a temporary bitmap for drawing
bitmap_argb32 tempbitmap(bmwidth + skewwidth, bmheight);
@@ -1506,76 +2551,10 @@ protected:
draw_segment_decimal(tempbitmap, bmwidth + segwidth/2, bmheight - segwidth/2, segwidth, BIT(state, 7) ? onpen : offpen);
// resample to the target size
- render_resample_argb_bitmap_hq(dest, tempbitmap, color());
- }
-};
-
-
-// 8-segment fluorescent (Gottlieb System 1)
-class layout_element::led8seg_gts1_component : public component
-{
-public:
- // construction/destruction
- led8seg_gts1_component(environment &env, util::xml::data_node const &compnode, const char *dirname)
- : component(env, compnode, dirname)
- {
- }
-
-protected:
- // overrides
- virtual int maxstate() const override { return 255; }
-
- virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
- {
- const rgb_t onpen = rgb_t(0xff,0xff,0xff,0xff);
- const rgb_t offpen = rgb_t(0x20,0xff,0xff,0xff);
- const rgb_t backpen = rgb_t(0x00,0x00,0x00,0x00);
-
- // sizes for computation
- int bmwidth = 250;
- int bmheight = 400;
- int segwidth = 40;
- int skewwidth = 40;
-
- // allocate a temporary bitmap for drawing
- bitmap_argb32 tempbitmap(bmwidth + skewwidth, bmheight);
- tempbitmap.fill(backpen);
-
- // top bar
- draw_segment_horizontal(tempbitmap, 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, 0 + segwidth/2, segwidth, (state & (1 << 0)) ? onpen : offpen);
-
- // top-right bar
- draw_segment_vertical(tempbitmap, 0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2, segwidth, (state & (1 << 1)) ? onpen : offpen);
-
- // bottom-right bar
- draw_segment_vertical(tempbitmap, bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2, segwidth, (state & (1 << 2)) ? onpen : offpen);
-
- // bottom bar
- draw_segment_horizontal(tempbitmap, 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, bmheight - segwidth/2, segwidth, (state & (1 << 3)) ? onpen : offpen);
-
- // bottom-left bar
- draw_segment_vertical(tempbitmap, bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2, segwidth, (state & (1 << 4)) ? onpen : offpen);
-
- // top-left bar
- draw_segment_vertical(tempbitmap, 0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2, segwidth, (state & (1 << 5)) ? onpen : offpen);
-
- // horizontal bars
- draw_segment_horizontal(tempbitmap, 0 + 2*segwidth/3, 2*bmwidth/3 - 2*segwidth/3, bmheight/2, segwidth, (state & (1 << 6)) ? onpen : offpen);
- draw_segment_horizontal(tempbitmap, 0 + 2*segwidth/3 + bmwidth/2, bmwidth - 2*segwidth/3, bmheight/2, segwidth, (state & (1 << 6)) ? onpen : offpen);
-
- // vertical bars
- draw_segment_vertical(tempbitmap, 0 + segwidth/3 - 8, bmheight/2 - segwidth/3 + 2, 2*bmwidth/3 - segwidth/2 - 4, segwidth + 8, backpen);
- draw_segment_vertical(tempbitmap, 0 + segwidth/3, bmheight/2 - segwidth/3, 2*bmwidth/3 - segwidth/2 - 4, segwidth, (state & (1 << 7)) ? onpen : offpen);
-
- draw_segment_vertical(tempbitmap, bmheight/2 + segwidth/3 - 2, bmheight - segwidth/3 + 8, 2*bmwidth/3 - segwidth/2 - 4, segwidth + 8, backpen);
- draw_segment_vertical(tempbitmap, bmheight/2 + segwidth/3, bmheight - segwidth/3, 2*bmwidth/3 - segwidth/2 - 4, segwidth, (state & (1 << 7)) ? onpen : offpen);
-
- // apply skew
- apply_skew(tempbitmap, 40);
-
- // resample to the target size
- render_resample_argb_bitmap_hq(dest, tempbitmap, color());
+ render_resample_argb_bitmap_hq(dest, tempbitmap, color(state));
}
+private:
+ int m_invert = 0;
};
@@ -1584,8 +2563,8 @@ class layout_element::led14seg_component : public component
{
public:
// construction/destruction
- led14seg_component(environment &env, util::xml::data_node const &compnode, const char *dirname)
- : component(env, compnode, dirname)
+ led14seg_component(environment &env, util::xml::data_node const &compnode)
+ : component(env, compnode)
{
}
@@ -1593,16 +2572,16 @@ protected:
// overrides
virtual int maxstate() const override { return 16383; }
- virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
+ virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
{
- const rgb_t onpen = rgb_t(0xff, 0xff, 0xff, 0xff);
- const rgb_t offpen = rgb_t(0x20, 0xff, 0xff, 0xff);
+ rgb_t const onpen = rgb_t(0xff, 0xff, 0xff, 0xff);
+ rgb_t const offpen = rgb_t(0x20, 0xff, 0xff, 0xff);
// sizes for computation
- int bmwidth = 250;
- int bmheight = 400;
- int segwidth = 40;
- int skewwidth = 40;
+ int const bmwidth = 250;
+ int const bmheight = 400;
+ int const segwidth = 40;
+ int const skewwidth = 40;
// allocate a temporary bitmap for drawing
bitmap_argb32 tempbitmap(bmwidth + skewwidth, bmheight);
@@ -1610,83 +2589,83 @@ protected:
// top bar
draw_segment_horizontal(tempbitmap,
- 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, 0 + segwidth/2,
- segwidth, (state & (1 << 0)) ? onpen : offpen);
+ 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, 0 + segwidth/2,
+ segwidth, (state & (1 << 0)) ? onpen : offpen);
// right-top bar
draw_segment_vertical(tempbitmap,
- 0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2,
- segwidth, (state & (1 << 1)) ? onpen : offpen);
+ 0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2,
+ segwidth, (state & (1 << 1)) ? onpen : offpen);
// right-bottom bar
draw_segment_vertical(tempbitmap,
- bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2,
- segwidth, (state & (1 << 2)) ? onpen : offpen);
+ bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2,
+ segwidth, (state & (1 << 2)) ? onpen : offpen);
// bottom bar
draw_segment_horizontal(tempbitmap,
- 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, bmheight - segwidth/2,
- segwidth, (state & (1 << 3)) ? onpen : offpen);
+ 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, bmheight - segwidth/2,
+ segwidth, (state & (1 << 3)) ? onpen : offpen);
// left-bottom bar
draw_segment_vertical(tempbitmap,
- bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2,
- segwidth, (state & (1 << 4)) ? onpen : offpen);
+ bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2,
+ segwidth, (state & (1 << 4)) ? onpen : offpen);
// left-top bar
draw_segment_vertical(tempbitmap,
- 0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2,
- segwidth, (state & (1 << 5)) ? onpen : offpen);
+ 0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2,
+ segwidth, (state & (1 << 5)) ? onpen : offpen);
// horizontal-middle-left bar
draw_segment_horizontal_caps(tempbitmap,
- 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight/2,
- segwidth, LINE_CAP_START, (state & (1 << 6)) ? onpen : offpen);
+ 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight/2,
+ segwidth, LINE_CAP_START, (state & (1 << 6)) ? onpen : offpen);
// horizontal-middle-right bar
draw_segment_horizontal_caps(tempbitmap,
- 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight/2,
- segwidth, LINE_CAP_END, (state & (1 << 7)) ? onpen : offpen);
+ 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight/2,
+ segwidth, LINE_CAP_END, (state & (1 << 7)) ? onpen : offpen);
// vertical-middle-top bar
draw_segment_vertical_caps(tempbitmap,
- 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, bmwidth/2,
- segwidth, LINE_CAP_NONE, (state & (1 << 8)) ? onpen : offpen);
+ 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, bmwidth/2,
+ segwidth, LINE_CAP_NONE, (state & (1 << 8)) ? onpen : offpen);
// vertical-middle-bottom bar
draw_segment_vertical_caps(tempbitmap,
- bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, bmwidth/2,
- segwidth, LINE_CAP_NONE, (state & (1 << 9)) ? onpen : offpen);
+ bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, bmwidth/2,
+ segwidth, LINE_CAP_NONE, (state & (1 << 9)) ? onpen : offpen);
// diagonal-left-bottom bar
draw_segment_diagonal_1(tempbitmap,
- 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
- bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
- segwidth, (state & (1 << 10)) ? onpen : offpen);
+ 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
+ bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
+ segwidth, (state & (1 << 10)) ? onpen : offpen);
// diagonal-left-top bar
draw_segment_diagonal_2(tempbitmap,
- 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
- 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
- segwidth, (state & (1 << 11)) ? onpen : offpen);
+ 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
+ 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
+ segwidth, (state & (1 << 11)) ? onpen : offpen);
// diagonal-right-top bar
draw_segment_diagonal_1(tempbitmap,
- bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
- 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
- segwidth, (state & (1 << 12)) ? onpen : offpen);
+ bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
+ 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
+ segwidth, (state & (1 << 12)) ? onpen : offpen);
// diagonal-right-bottom bar
draw_segment_diagonal_2(tempbitmap,
- bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
- bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
- segwidth, (state & (1 << 13)) ? onpen : offpen);
+ bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
+ bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
+ segwidth, (state & (1 << 13)) ? onpen : offpen);
// apply skew
apply_skew(tempbitmap, 40);
// resample to the target size
- render_resample_argb_bitmap_hq(dest, tempbitmap, color());
+ render_resample_argb_bitmap_hq(dest, tempbitmap, color(state));
}
};
@@ -1696,8 +2675,8 @@ class layout_element::led16seg_component : public component
{
public:
// construction/destruction
- led16seg_component(environment &env, util::xml::data_node const &compnode, const char *dirname)
- : component(env, compnode, dirname)
+ led16seg_component(environment &env, util::xml::data_node const &compnode)
+ : component(env, compnode)
{
}
@@ -1705,7 +2684,7 @@ protected:
// overrides
virtual int maxstate() const override { return 65535; }
- virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
+ virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
{
const rgb_t onpen = rgb_t(0xff, 0xff, 0xff, 0xff);
const rgb_t offpen = rgb_t(0x20, 0xff, 0xff, 0xff);
@@ -1808,7 +2787,7 @@ protected:
apply_skew(tempbitmap, 40);
// resample to the target size
- render_resample_argb_bitmap_hq(dest, tempbitmap, color());
+ render_resample_argb_bitmap_hq(dest, tempbitmap, color(state));
}
};
@@ -1818,8 +2797,8 @@ class layout_element::led14segsc_component : public component
{
public:
// construction/destruction
- led14segsc_component(environment &env, util::xml::data_node const &compnode, const char *dirname)
- : component(env, compnode, dirname)
+ led14segsc_component(environment &env, util::xml::data_node const &compnode)
+ : component(env, compnode)
{
}
@@ -1827,16 +2806,16 @@ protected:
// overrides
virtual int maxstate() const override { return 65535; }
- virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
+ virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
{
- const rgb_t onpen = rgb_t(0xff, 0xff, 0xff, 0xff);
- const rgb_t offpen = rgb_t(0x20, 0xff, 0xff, 0xff);
+ rgb_t const onpen = rgb_t(0xff, 0xff, 0xff, 0xff);
+ rgb_t const offpen = rgb_t(0x20, 0xff, 0xff, 0xff);
// sizes for computation
- int bmwidth = 250;
- int bmheight = 400;
- int segwidth = 40;
- int skewwidth = 40;
+ int const bmwidth = 250;
+ int const bmheight = 400;
+ int const segwidth = 40;
+ int const skewwidth = 40;
// allocate a temporary bitmap for drawing, adding some extra space for the tail
bitmap_argb32 tempbitmap(bmwidth + skewwidth, bmheight + segwidth);
@@ -1844,92 +2823,94 @@ protected:
// top bar
draw_segment_horizontal(tempbitmap,
- 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, 0 + segwidth/2,
- segwidth, (state & (1 << 0)) ? onpen : offpen);
+ 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, 0 + segwidth/2,
+ segwidth, (state & (1 << 0)) ? onpen : offpen);
// right-top bar
draw_segment_vertical(tempbitmap,
- 0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2,
- segwidth, (state & (1 << 1)) ? onpen : offpen);
+ 0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2,
+ segwidth, (state & (1 << 1)) ? onpen : offpen);
// right-bottom bar
draw_segment_vertical(tempbitmap,
- bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2,
- segwidth, (state & (1 << 2)) ? onpen : offpen);
+ bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2,
+ segwidth, (state & (1 << 2)) ? onpen : offpen);
// bottom bar
draw_segment_horizontal(tempbitmap,
- 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, bmheight - segwidth/2,
- segwidth, (state & (1 << 3)) ? onpen : offpen);
+ 0 + 2*segwidth/3, bmwidth - 2*segwidth/3, bmheight - segwidth/2,
+ segwidth, (state & (1 << 3)) ? onpen : offpen);
// left-bottom bar
draw_segment_vertical(tempbitmap,
- bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2,
- segwidth, (state & (1 << 4)) ? onpen : offpen);
+ bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2,
+ segwidth, (state & (1 << 4)) ? onpen : offpen);
// left-top bar
draw_segment_vertical(tempbitmap,
- 0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2,
- segwidth, (state & (1 << 5)) ? onpen : offpen);
+ 0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2,
+ segwidth, (state & (1 << 5)) ? onpen : offpen);
// horizontal-middle-left bar
draw_segment_horizontal_caps(tempbitmap,
- 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight/2,
- segwidth, LINE_CAP_START, (state & (1 << 6)) ? onpen : offpen);
+ 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight/2,
+ segwidth, LINE_CAP_START, (state & (1 << 6)) ? onpen : offpen);
// horizontal-middle-right bar
draw_segment_horizontal_caps(tempbitmap,
- 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight/2,
- segwidth, LINE_CAP_END, (state & (1 << 7)) ? onpen : offpen);
+ 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight/2,
+ segwidth, LINE_CAP_END, (state & (1 << 7)) ? onpen : offpen);
// vertical-middle-top bar
draw_segment_vertical_caps(tempbitmap,
- 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, bmwidth/2,
- segwidth, LINE_CAP_NONE, (state & (1 << 8)) ? onpen : offpen);
+ 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, bmwidth/2,
+ segwidth, LINE_CAP_NONE, (state & (1 << 8)) ? onpen : offpen);
// vertical-middle-bottom bar
draw_segment_vertical_caps(tempbitmap,
- bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, bmwidth/2,
- segwidth, LINE_CAP_NONE, (state & (1 << 9)) ? onpen : offpen);
+ bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, bmwidth/2,
+ segwidth, LINE_CAP_NONE, (state & (1 << 9)) ? onpen : offpen);
// diagonal-left-bottom bar
draw_segment_diagonal_1(tempbitmap,
- 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
- bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
- segwidth, (state & (1 << 10)) ? onpen : offpen);
+ 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
+ bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
+ segwidth, (state & (1 << 10)) ? onpen : offpen);
// diagonal-left-top bar
draw_segment_diagonal_2(tempbitmap,
- 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
- 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
- segwidth, (state & (1 << 11)) ? onpen : offpen);
+ 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
+ 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
+ segwidth, (state & (1 << 11)) ? onpen : offpen);
// diagonal-right-top bar
draw_segment_diagonal_1(tempbitmap,
- bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
- 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
- segwidth, (state & (1 << 12)) ? onpen : offpen);
+ bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
+ 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
+ segwidth, (state & (1 << 12)) ? onpen : offpen);
// diagonal-right-bottom bar
draw_segment_diagonal_2(tempbitmap,
- bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
- bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
- segwidth, (state & (1 << 13)) ? onpen : offpen);
+ bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
+ bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
+ segwidth, (state & (1 << 13)) ? onpen : offpen);
// apply skew
apply_skew(tempbitmap, 40);
// comma tail
draw_segment_diagonal_1(tempbitmap,
- bmwidth - (segwidth/2), bmwidth + segwidth,
- bmheight - (segwidth), bmheight + segwidth*1.5,
- segwidth/2, (state & (1 << 15)) ? onpen : offpen);
+ bmwidth - (segwidth/2), bmwidth + segwidth,
+ bmheight - (segwidth), bmheight + segwidth*1.5,
+ segwidth/2, (state & (1 << 15)) ? onpen : offpen);
// decimal point
- draw_segment_decimal(tempbitmap, bmwidth + segwidth/2, bmheight - segwidth/2, segwidth, (state & (1 << 14)) ? onpen : offpen);
+ draw_segment_decimal(tempbitmap,
+ bmwidth + segwidth/2, bmheight - segwidth/2,
+ segwidth, (state & (1 << 14)) ? onpen : offpen);
// resample to the target size
- render_resample_argb_bitmap_hq(dest, tempbitmap, color());
+ render_resample_argb_bitmap_hq(dest, tempbitmap, color(state));
}
};
@@ -1939,8 +2920,8 @@ class layout_element::led16segsc_component : public component
{
public:
// construction/destruction
- led16segsc_component(environment &env, util::xml::data_node const &compnode, const char *dirname)
- : component(env, compnode, dirname)
+ led16segsc_component(environment &env, util::xml::data_node const &compnode)
+ : component(env, compnode)
{
}
@@ -1948,16 +2929,16 @@ protected:
// overrides
virtual int maxstate() const override { return 262143; }
- virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
+ virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
{
- const rgb_t onpen = rgb_t(0xff, 0xff, 0xff, 0xff);
- const rgb_t offpen = rgb_t(0x20, 0xff, 0xff, 0xff);
+ rgb_t const onpen = rgb_t(0xff, 0xff, 0xff, 0xff);
+ rgb_t const offpen = rgb_t(0x20, 0xff, 0xff, 0xff);
// sizes for computation
- int bmwidth = 250;
- int bmheight = 400;
- int segwidth = 40;
- int skewwidth = 40;
+ int const bmwidth = 250;
+ int const bmheight = 400;
+ int const segwidth = 40;
+ int const skewwidth = 40;
// allocate a temporary bitmap for drawing
bitmap_argb32 tempbitmap(bmwidth + skewwidth, bmheight + segwidth);
@@ -1965,144 +2946,104 @@ protected:
// top-left bar
draw_segment_horizontal_caps(tempbitmap,
- 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, 0 + segwidth/2,
- segwidth, LINE_CAP_START, (state & (1 << 0)) ? onpen : offpen);
+ 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, 0 + segwidth/2,
+ segwidth, LINE_CAP_START, (state & (1 << 0)) ? onpen : offpen);
// top-right bar
draw_segment_horizontal_caps(tempbitmap,
- 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, 0 + segwidth/2,
- segwidth, LINE_CAP_END, (state & (1 << 1)) ? onpen : offpen);
+ 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, 0 + segwidth/2,
+ segwidth, LINE_CAP_END, (state & (1 << 1)) ? onpen : offpen);
// right-top bar
draw_segment_vertical(tempbitmap,
- 0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2,
- segwidth, (state & (1 << 2)) ? onpen : offpen);
+ 0 + 2*segwidth/3, bmheight/2 - segwidth/3, bmwidth - segwidth/2,
+ segwidth, (state & (1 << 2)) ? onpen : offpen);
// right-bottom bar
draw_segment_vertical(tempbitmap,
- bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2,
- segwidth, (state & (1 << 3)) ? onpen : offpen);
+ bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, bmwidth - segwidth/2,
+ segwidth, (state & (1 << 3)) ? onpen : offpen);
// bottom-right bar
draw_segment_horizontal_caps(tempbitmap,
- 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight - segwidth/2,
- segwidth, LINE_CAP_END, (state & (1 << 4)) ? onpen : offpen);
+ 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight - segwidth/2,
+ segwidth, LINE_CAP_END, (state & (1 << 4)) ? onpen : offpen);
// bottom-left bar
draw_segment_horizontal_caps(tempbitmap,
- 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight - segwidth/2,
- segwidth, LINE_CAP_START, (state & (1 << 5)) ? onpen : offpen);
+ 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight - segwidth/2,
+ segwidth, LINE_CAP_START, (state & (1 << 5)) ? onpen : offpen);
// left-bottom bar
draw_segment_vertical(tempbitmap,
- bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2,
- segwidth, (state & (1 << 6)) ? onpen : offpen);
+ bmheight/2 + segwidth/3, bmheight - 2*segwidth/3, 0 + segwidth/2,
+ segwidth, (state & (1 << 6)) ? onpen : offpen);
// left-top bar
draw_segment_vertical(tempbitmap,
- 0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2,
- segwidth, (state & (1 << 7)) ? onpen : offpen);
+ 0 + 2*segwidth/3, bmheight/2 - segwidth/3, 0 + segwidth/2,
+ segwidth, (state & (1 << 7)) ? onpen : offpen);
// horizontal-middle-left bar
draw_segment_horizontal_caps(tempbitmap,
- 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight/2,
- segwidth, LINE_CAP_START, (state & (1 << 8)) ? onpen : offpen);
+ 0 + 2*segwidth/3, bmwidth/2 - segwidth/10, bmheight/2,
+ segwidth, LINE_CAP_START, (state & (1 << 8)) ? onpen : offpen);
// horizontal-middle-right bar
draw_segment_horizontal_caps(tempbitmap,
- 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight/2,
- segwidth, LINE_CAP_END, (state & (1 << 9)) ? onpen : offpen);
+ 0 + bmwidth/2 + segwidth/10, bmwidth - 2*segwidth/3, bmheight/2,
+ segwidth, LINE_CAP_END, (state & (1 << 9)) ? onpen : offpen);
// vertical-middle-top bar
draw_segment_vertical_caps(tempbitmap,
- 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, bmwidth/2,
- segwidth, LINE_CAP_NONE, (state & (1 << 10)) ? onpen : offpen);
+ 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3, bmwidth/2,
+ segwidth, LINE_CAP_NONE, (state & (1 << 10)) ? onpen : offpen);
// vertical-middle-bottom bar
draw_segment_vertical_caps(tempbitmap,
- bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, bmwidth/2,
- segwidth, LINE_CAP_NONE, (state & (1 << 11)) ? onpen : offpen);
+ bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3, bmwidth/2,
+ segwidth, LINE_CAP_NONE, (state & (1 << 11)) ? onpen : offpen);
// diagonal-left-bottom bar
draw_segment_diagonal_1(tempbitmap,
- 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
- bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
- segwidth, (state & (1 << 12)) ? onpen : offpen);
+ 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
+ bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
+ segwidth, (state & (1 << 12)) ? onpen : offpen);
// diagonal-left-top bar
draw_segment_diagonal_2(tempbitmap,
- 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
- 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
- segwidth, (state & (1 << 13)) ? onpen : offpen);
+ 0 + segwidth + segwidth/5, bmwidth/2 - segwidth/2 - segwidth/5,
+ 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
+ segwidth, (state & (1 << 13)) ? onpen : offpen);
// diagonal-right-top bar
draw_segment_diagonal_1(tempbitmap,
- bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
- 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
- segwidth, (state & (1 << 14)) ? onpen : offpen);
+ bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
+ 0 + segwidth + segwidth/3, bmheight/2 - segwidth/2 - segwidth/3,
+ segwidth, (state & (1 << 14)) ? onpen : offpen);
// diagonal-right-bottom bar
draw_segment_diagonal_2(tempbitmap,
- bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
- bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
- segwidth, (state & (1 << 15)) ? onpen : offpen);
-
- // comma tail
- draw_segment_diagonal_1(tempbitmap,
- bmwidth - (segwidth/2), bmwidth + segwidth,
- bmheight - (segwidth), bmheight + segwidth*1.5,
- segwidth/2, (state & (1 << 17)) ? onpen : offpen);
-
- // decimal point (draw last for priority)
- draw_segment_decimal(tempbitmap, bmwidth + segwidth/2, bmheight - segwidth/2, segwidth, (state & (1 << 16)) ? onpen : offpen);
+ bmwidth/2 + segwidth/2 + segwidth/5, bmwidth - segwidth - segwidth/5,
+ bmheight/2 + segwidth/2 + segwidth/3, bmheight - segwidth - segwidth/3,
+ segwidth, (state & (1 << 15)) ? onpen : offpen);
// apply skew
apply_skew(tempbitmap, 40);
- // resample to the target size
- render_resample_argb_bitmap_hq(dest, tempbitmap, color());
- }
-};
-
-
-// row of dots for a dotmatrix
-class layout_element::dotmatrix_component : public component
-{
-public:
- // construction/destruction
- dotmatrix_component(int dots, environment &env, util::xml::data_node const &compnode, const char *dirname)
- : component(env, compnode, dirname)
- , m_dots(dots)
- {
- }
-
-protected:
- // overrides
- virtual int maxstate() const override { return (1 << m_dots) - 1; }
-
- virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
- {
- const rgb_t onpen = rgb_t(0xff, 0xff, 0xff, 0xff);
- const rgb_t offpen = rgb_t(0xff, 0x20, 0x20, 0x20);
-
- // sizes for computation
- int bmheight = 300;
- int dotwidth = 250;
-
- // allocate a temporary bitmap for drawing
- bitmap_argb32 tempbitmap(dotwidth*m_dots, bmheight);
- tempbitmap.fill(rgb_t(0xff, 0x00, 0x00, 0x00));
+ // comma tail
+ draw_segment_diagonal_1(tempbitmap,
+ bmwidth - (segwidth/2), bmwidth + segwidth, bmheight - (segwidth), bmheight + segwidth*1.5,
+ segwidth/2, (state & (1 << 17)) ? onpen : offpen);
- for (int i = 0; i < m_dots; i++)
- draw_segment_decimal(tempbitmap, ((dotwidth / 2) + (i * dotwidth)), bmheight / 2, dotwidth, BIT(state, i) ? onpen : offpen);
+ // decimal point (draw last for priority)
+ draw_segment_decimal(tempbitmap,
+ bmwidth + segwidth/2, bmheight - segwidth/2,
+ segwidth, (state & (1 << 16)) ? onpen : offpen);
// resample to the target size
- render_resample_argb_bitmap_hq(dest, tempbitmap, color());
+ render_resample_argb_bitmap_hq(dest, tempbitmap, color(state));
}
-
-private:
- // internal state
- int m_dots;
};
@@ -2111,8 +3052,8 @@ class layout_element::simplecounter_component : public component
{
public:
// construction/destruction
- simplecounter_component(environment &env, util::xml::data_node const &compnode, const char *dirname)
- : component(env, compnode, dirname)
+ simplecounter_component(environment &env, util::xml::data_node const &compnode)
+ : component(env, compnode)
, m_digits(env.get_attribute_int(compnode, "digits", 2))
, m_textalign(env.get_attribute_int(compnode, "align", 0))
, m_maxstate(env.get_attribute_int(compnode, "maxstate", 999))
@@ -2123,12 +3064,10 @@ protected:
// overrides
virtual int maxstate() const override { return m_maxstate; }
- virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
+ virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
{
- render_font *font = machine.render().font_alloc("default");
- std::string temp = string_format("%0*d", m_digits, state);
- draw_text(*font, dest, bounds, temp.c_str(), m_textalign);
- machine.render().font_free(font);
+ auto font = machine.render().font_alloc("default");
+ draw_text(*font, dest, bounds, string_format("%0*d", m_digits, state), m_textalign, color(state));
}
private:
@@ -2146,49 +3085,32 @@ class layout_element::reel_component : public component
public:
// construction/destruction
- reel_component(environment &env, util::xml::data_node const &compnode, const char *dirname)
- : component(env, compnode, dirname)
+ reel_component(environment &env, util::xml::data_node const &compnode)
+ : component(env, compnode)
+ , m_searchpath(env.search_path() ? env.search_path() : "")
+ , m_dirname(env.directory_name() ? env.directory_name() : "")
{
- for (auto & elem : m_hasalpha)
- elem = false;
+ osd_printf_warning("Warning: layout file contains deprecated reel component\n");
- std::string symbollist = env.get_attribute_string(compnode, "symbollist", "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15");
+ std::string_view symbollist = env.get_attribute_string(compnode, "symbollist", "0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15");
// split out position names from string and figure out our number of symbols
- int location;
m_numstops = 0;
- location=symbollist.find(",");
- while (location!=-1)
+ for (std::string_view::size_type location = symbollist.find(','); std::string_view::npos != location; location = symbollist.find(','))
{
- m_stopnames[m_numstops] = symbollist;
- m_stopnames[m_numstops] = m_stopnames[m_numstops].substr(0, location);
- symbollist = symbollist.substr(location+1, symbollist.length()-(location-1));
+ m_stopnames[m_numstops] = symbollist.substr(0, location);
+ symbollist.remove_prefix(location + 1);
m_numstops++;
- location=symbollist.find(",");
}
m_stopnames[m_numstops++] = symbollist;
- // careful, dirname is nullptr if we're coming from internal layout, and our string assignment doesn't like that
- if (dirname != nullptr)
- m_dirname = dirname;
-
- for (int i=0;i<m_numstops;i++)
+ for (int i = 0; i < m_numstops; i++)
{
- location=m_stopnames[i].find(":");
- if (location!=-1)
+ std::string::size_type const location = m_stopnames[i].find(':');
+ if (location != std::string::npos)
{
- m_imagefile[i] = m_stopnames[i];
- m_stopnames[i] = m_stopnames[i].substr(0, location);
- m_imagefile[i] = m_imagefile[i].substr(location+1, m_imagefile[i].length()-(location-1));
-
- //m_alphafile[i] =
- m_file[i] = std::make_unique<emu_file>(env.machine().options().art_path(), OPEN_FLAG_READ);
- }
- else
- {
- //m_imagefile[i] = 0;
- //m_alphafile[i] = 0;
- m_file[i].reset();
+ m_imagefile[i] = m_stopnames[i].substr(location + 1);
+ m_stopnames[i].erase(location);
}
}
@@ -2198,10 +3120,21 @@ public:
m_beltreel = env.get_attribute_int(compnode, "beltreel", 0);
}
-protected:
// overrides
+ virtual void preload(running_machine &machine) override
+ {
+ for (int i = 0; i < m_numstops; i++)
+ {
+ if (!m_imagefile[i].empty() && !m_bitmap[i].valid())
+ load_reel_bitmap(i);
+ }
+
+ }
+
+protected:
virtual int maxstate() const override { return 65535; }
- virtual void draw(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
+
+ virtual void draw_aligned(running_machine &machine, bitmap_argb32 &dest, const rectangle &bounds, int state) override
{
if (m_beltreel)
{
@@ -2215,22 +3148,19 @@ protected:
// shift the reels a bit based on this param, allows fine tuning
int use_state = (state + m_stateoffset) % max_state_used;
- // compute premultiplied colors
- u32 r = color().r * 255.0f;
- u32 g = color().g * 255.0f;
- u32 b = color().b * 255.0f;
- u32 a = color().a * 255.0f;
-
- // get the width of the string
- render_font *font = machine.render().font_alloc("default");
- float aspect = 1.0f;
- s32 width;
+ // compute premultiplied color
+ render_color const c(color(state));
+ u32 const r = c.r * 255.0f;
+ u32 const g = c.g * 255.0f;
+ u32 const b = c.b * 255.0f;
+ u32 const a = c.a * 255.0f;
int curry = 0;
int num_shown = m_numsymbolsvisible;
int ourheight = bounds.height();
+ auto font = machine.render().font_alloc("default");
for (int fruit = 0;fruit<m_numstops;fruit++)
{
int basey;
@@ -2252,50 +3182,34 @@ protected:
int endpos = basey+ourheight/num_shown;
- // only render the symbol / text if it's atually in view because the code is SLOW
+ // only render the symbol / text if it's actually in view because the code is SLOW
if ((endpos >= bounds.top()) && (basey <= bounds.bottom()))
{
- while (1)
- {
- width = font->string_width(ourheight / num_shown, aspect, m_stopnames[fruit].c_str());
- if (width < bounds.width())
- break;
- aspect *= 0.9f;
- }
-
- s32 curx;
- curx = bounds.left() + (bounds.width() - width) / 2;
-
- if (m_file[fruit])
- if (!m_bitmap[fruit].valid())
- load_reel_bitmap(fruit);
+ if (!m_imagefile[fruit].empty() && !m_bitmap[fruit].valid())
+ load_reel_bitmap(fruit);
- if (m_file[fruit]) // render gfx
+ if (m_bitmap[fruit].valid()) // render gfx
{
bitmap_argb32 tempbitmap2(dest.width(), ourheight/num_shown);
+ render_resample_argb_bitmap_hq(tempbitmap2, m_bitmap[fruit], c);
- if (m_bitmap[fruit].valid())
+ for (int y = 0; y < ourheight/num_shown; y++)
{
- render_resample_argb_bitmap_hq(tempbitmap2, m_bitmap[fruit], color());
+ int effy = basey + y;
- for (int y = 0; y < ourheight/num_shown; y++)
+ if (effy >= bounds.top() && effy <= bounds.bottom())
{
- int effy = basey + y;
-
- if (effy >= bounds.top() && effy <= bounds.bottom())
+ u32 const *const src = &tempbitmap2.pix(y);
+ u32 *const d = &dest.pix(effy);
+ for (int x = 0; x < dest.width(); x++)
{
- u32 *src = &tempbitmap2.pix32(y);
- u32 *d = &dest.pix32(effy);
- for (int x = 0; x < dest.width(); x++)
+ int effx = x;
+ if (effx >= bounds.left() && effx <= bounds.right())
{
- int effx = x;
- if (effx >= bounds.left() && effx <= bounds.right())
+ u32 spix = rgb_t(src[x]).a();
+ if (spix != 0)
{
- u32 spix = rgb_t(src[x]).a();
- if (spix != 0)
- {
- d[effx] = src[x];
- }
+ d[effx] = src[x];
}
}
}
@@ -2307,15 +3221,26 @@ protected:
// allocate a temporary bitmap
bitmap_argb32 tempbitmap(dest.width(), dest.height());
- const char *origs = m_stopnames[fruit].c_str();
- const char *ends = origs + strlen(origs);
- const char *s = origs;
- char32_t schar;
+ // get the width of the string
+ float aspect = 1.0f;
+ s32 width;
+
+ while (1)
+ {
+ width = font->string_width(ourheight / num_shown, aspect, m_stopnames[fruit]);
+ if (width < bounds.width())
+ break;
+ aspect *= 0.95f;
+ }
+
+ float curx = bounds.left() + (bounds.width() - width) / 2.0f;
// loop over characters
- while (*s != 0)
+ std::string_view s = m_stopnames[fruit];
+ while (!s.empty())
{
- int scharcount = uchar_from_utf8(&schar, s, ends - s);
+ char32_t schar;
+ int scharcount = uchar_from_utf8(&schar, s);
if (scharcount == -1)
break;
@@ -2331,11 +3256,11 @@ protected:
if (effy >= bounds.top() && effy <= bounds.bottom())
{
- u32 *src = &tempbitmap.pix32(y);
- u32 *d = &dest.pix32(effy);
+ u32 const *const src = &tempbitmap.pix(y);
+ u32 *const d = &dest.pix(effy);
for (int x = 0; x < chbounds.width(); x++)
{
- int effx = curx + x + chbounds.left();
+ int effx = int(curx) + x + chbounds.left();
if (effx >= bounds.left() && effx <= bounds.right())
{
u32 spix = rgb_t(src[x]).a();
@@ -2355,7 +3280,7 @@ protected:
// advance in the X direction
curx += font->char_width(ourheight/num_shown, aspect, schar);
- s += scharcount;
+ s.remove_prefix(scharcount);
}
}
}
@@ -2364,7 +3289,6 @@ protected:
}
// free the temporary bitmap and font
- machine.render().font_free(font);
}
private:
@@ -2376,21 +3300,19 @@ private:
// shift the reels a bit based on this param, allows fine tuning
int use_state = (state + m_stateoffset) % max_state_used;
- // compute premultiplied colors
- u32 r = color().r * 255.0f;
- u32 g = color().g * 255.0f;
- u32 b = color().b * 255.0f;
- u32 a = color().a * 255.0f;
+ // compute premultiplied color
+ render_color const c(color(state));
+ u32 const r = c.r * 255.0f;
+ u32 const g = c.g * 255.0f;
+ u32 const b = c.b * 255.0f;
+ u32 const a = c.a * 255.0f;
- // get the width of the string
- render_font *font = machine.render().font_alloc("default");
- float aspect = 1.0f;
- s32 width;
int currx = 0;
int num_shown = m_numsymbolsvisible;
int ourwidth = bounds.width();
+ auto font = machine.render().font_alloc("default");
for (int fruit = 0;fruit<m_numstops;fruit++)
{
int basex;
@@ -2411,71 +3333,65 @@ private:
int endpos = basex+(ourwidth/num_shown);
- // only render the symbol / text if it's atually in view because the code is SLOW
+ // only render the symbol / text if it's actually in view because the code is SLOW
if ((endpos >= bounds.left()) && (basex <= bounds.right()))
{
- while (1)
- {
- width = font->string_width(dest.height(), aspect, m_stopnames[fruit].c_str());
- if (width < bounds.width())
- break;
- aspect *= 0.9f;
- }
-
- s32 curx;
- curx = bounds.left();
-
- if (m_file[fruit])
- if (!m_bitmap[fruit].valid())
- load_reel_bitmap(fruit);
+ if (!m_imagefile[fruit].empty() && !m_bitmap[fruit].valid())
+ load_reel_bitmap(fruit);
- if (m_file[fruit]) // render gfx
+ if (m_bitmap[fruit].valid()) // render gfx
{
bitmap_argb32 tempbitmap2(ourwidth/num_shown, dest.height());
+ render_resample_argb_bitmap_hq(tempbitmap2, m_bitmap[fruit], c);
- if (m_bitmap[fruit].valid())
+ for (int y = 0; y < dest.height(); y++)
{
- render_resample_argb_bitmap_hq(tempbitmap2, m_bitmap[fruit], color());
+ int effy = y;
- for (int y = 0; y < dest.height(); y++)
+ if (effy >= bounds.top() && effy <= bounds.bottom())
{
- int effy = y;
-
- if (effy >= bounds.top() && effy <= bounds.bottom())
+ u32 const *const src = &tempbitmap2.pix(y);
+ u32 *const d = &dest.pix(effy);
+ for (int x = 0; x < ourwidth/num_shown; x++)
{
- u32 *src = &tempbitmap2.pix32(y);
- u32 *d = &dest.pix32(effy);
- for (int x = 0; x < ourwidth/num_shown; x++)
+ int effx = basex + x;
+ if (effx >= bounds.left() && effx <= bounds.right())
{
- int effx = basex + x;
- if (effx >= bounds.left() && effx <= bounds.right())
+ u32 spix = rgb_t(src[x]).a();
+ if (spix != 0)
{
- u32 spix = rgb_t(src[x]).a();
- if (spix != 0)
- {
- d[effx] = src[x];
- }
+ d[effx] = src[x];
}
}
}
-
}
+
}
}
else // render text (fallback)
{
+ // get the width of the string
+ float aspect = 1.0f;
+ s32 width;
+ while (1)
+ {
+ width = font->string_width(dest.height(), aspect, m_stopnames[fruit]);
+ if (width < bounds.width())
+ break;
+ aspect *= 0.95f;
+ }
+
+ float curx = bounds.left();
+
// allocate a temporary bitmap
bitmap_argb32 tempbitmap(dest.width(), dest.height());
- const char *origs = m_stopnames[fruit].c_str();
- const char *ends = origs + strlen(origs);
- const char *s = origs;
- char32_t schar;
-
// loop over characters
- while (*s != 0)
+ std::string_view s = m_stopnames[fruit];
+ while (!s.empty())
{
- int scharcount = uchar_from_utf8(&schar, s, ends - s);
+ char32_t schar;
+ int scharcount = uchar_from_utf8(&schar, s);
if (scharcount == -1)
break;
@@ -2491,11 +3407,11 @@ private:
if (effy >= bounds.top() && effy <= bounds.bottom())
{
- u32 *src = &tempbitmap.pix32(y);
- u32 *d = &dest.pix32(effy);
+ u32 const *const src = &tempbitmap.pix(y);
+ u32 *const d = &dest.pix(effy);
for (int x = 0; x < chbounds.width(); x++)
{
- int effx = basex + curx + x;
+ int effx = basex + int(curx) + x;
if (effx >= bounds.left() && effx <= bounds.right())
{
u32 spix = rgb_t(src[x]).a();
@@ -2515,7 +3431,7 @@ private:
// advance in the X direction
curx += font->char_width(dest.height(), aspect, schar);
- s += scharcount;
+ s.remove_prefix(scharcount);
}
}
}
@@ -2524,35 +3440,31 @@ private:
}
// free the temporary bitmap and font
- machine.render().font_free(font);
}
void load_reel_bitmap(int number)
{
- // 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());
+ emu_file file(m_searchpath.empty() ? m_dirname : m_searchpath, OPEN_FLAG_READ);
+ std::string filename;
+ if (!m_searchpath.empty())
+ filename = m_dirname;
+ util::path_append(filename, m_imagefile[number]);
- // load the alpha bitmap if specified
- //if (m_bitmap[number].valid() && m_alphafile[number])
- // render_load_png(m_bitmap[number], *m_file[number], m_dirname, m_alphafile[number], true);
+ // load the basic bitmap
+ if (!file.open(filename))
+ render_load_png(m_bitmap[number], file);
// if we can't load the bitmap just use text rendering
if (!m_bitmap[number].valid())
- {
- // fallback to text rendering
- m_file[number].reset();
- }
+ m_imagefile[number].clear();
}
// internal state
bitmap_argb32 m_bitmap[MAX_BITMAPS]; // source bitmap for images
+ std::string m_searchpath; // asset search path (for lazy loading)
std::string m_dirname; // directory name of image file (for lazy loading)
- std::unique_ptr<emu_file> m_file[MAX_BITMAPS]; // file object for reading image/alpha files
std::string m_imagefile[MAX_BITMAPS]; // name of the image file (for lazy loading)
- std::string m_alphafile[MAX_BITMAPS]; // name of the alpha file (for lazy loading)
- bool m_hasalpha[MAX_BITMAPS]; // is there any alpha component present?
// basically made up of multiple text strings / gfx
int m_numstops;
@@ -2569,21 +3481,9 @@ private:
//-------------------------------------------------
template <typename T>
-layout_element::component::ptr layout_element::make_component(environment &env, util::xml::data_node const &compnode, const char *dirname)
-{
- return std::make_unique<T>(env, compnode, dirname);
-}
-
-
-//-------------------------------------------------
-// make_component - create dotmatrix component
-// with given vertical resolution
-//-------------------------------------------------
-
-template <int D>
-layout_element::component::ptr layout_element::make_dotmatrix_component(environment &env, util::xml::data_node const &compnode, const char *dirname)
+layout_element::component::ptr layout_element::make_component(environment &env, util::xml::data_node const &compnode)
{
- return std::make_unique<dotmatrix_component>(D, env, compnode, dirname);
+ return std::make_unique<T>(env, compnode);
}
@@ -2616,7 +3516,7 @@ layout_element::texture::texture(texture &&that) : texture()
layout_element::texture::~texture()
{
- if (m_element != nullptr)
+ if (m_element)
m_element->machine().render().texture_free(m_texture);
}
@@ -2644,11 +3544,35 @@ layout_element::texture &layout_element::texture::operator=(texture &&that)
// component - constructor
//-------------------------------------------------
-layout_element::component::component(environment &env, util::xml::data_node const &compnode, const char *dirname)
- : m_state(env.get_attribute_int(compnode, "state", -1))
- , m_color(env.parse_color(compnode.get_child("color")))
+layout_element::component::component(environment &env, util::xml::data_node const &compnode)
+ : m_statemask(env.get_attribute_int(compnode, "statemask", env.get_attribute_string(compnode, "state").empty() ? 0 : ~0))
+ , m_stateval(env.get_attribute_int(compnode, "state", m_statemask) & m_statemask)
{
- env.parse_bounds(compnode.get_child("bounds"), m_bounds);
+ for (util::xml::data_node const *child = compnode.get_first_child(); child; child = child->get_next_sibling())
+ {
+ if (!strcmp(child->get_name(), "bounds"))
+ {
+ if (!add_bounds_step(env, m_bounds, *child))
+ {
+ throw layout_syntax_error(
+ util::string_format(
+ "%s component has duplicate bounds for state",
+ compnode.get_name()));
+ }
+ }
+ else if (!strcmp(child->get_name(), "color"))
+ {
+ if (!add_color_step(env, m_color, *child))
+ {
+ throw layout_syntax_error(
+ util::string_format(
+ "%s component has duplicate color for state",
+ compnode.get_name()));
+ }
+ }
+ }
+ set_bounds_deltas(m_bounds);
+ set_color_deltas(m_color);
}
@@ -2658,25 +3582,142 @@ layout_element::component::component(environment &env, util::xml::data_node cons
void layout_element::component::normalize_bounds(float xoffs, float yoffs, float xscale, float yscale)
{
- m_bounds.x0 = (m_bounds.x0 - xoffs) * xscale;
- m_bounds.x1 = (m_bounds.x1 - xoffs) * xscale;
- m_bounds.y0 = (m_bounds.y0 - yoffs) * yscale;
- m_bounds.y1 = (m_bounds.y1 - yoffs) * yscale;
+ ::normalize_bounds(m_bounds, 0.0F, 0.0F, xoffs, yoffs, xscale, yscale);
}
//-------------------------------------------------
-// draw_text - draw text in the specified color
+// statewrap - get state wraparound requirements
+//-------------------------------------------------
+
+std::pair<int, bool> layout_element::component::statewrap() const
+{
+ int result(0);
+ bool fold;
+ auto const adjustmask =
+ [&result, &fold] (int val, int mask)
+ {
+ assert(!(val & ~mask));
+ auto const splatright =
+ [] (int x)
+ {
+ for (unsigned shift = 1; (sizeof(x) * 4) >= shift; shift <<= 1)
+ x |= (x >> shift);
+ return x;
+ };
+ int const unfolded(splatright(mask));
+ int const folded(splatright(~mask | splatright(val)));
+ if (unsigned(folded) < unsigned(unfolded))
+ {
+ result |= folded;
+ fold = true;
+ }
+ else
+ {
+ result |= unfolded;
+ }
+ };
+ adjustmask(stateval(), statemask());
+ int max(maxstate());
+ if (m_bounds.size() > 1U)
+ max = (std::max)(max, m_bounds.back().state);
+ if (m_color.size() > 1U)
+ max = (std::max)(max, m_color.back().state);
+ if (0 <= max)
+ adjustmask(max, ~0);
+ return std::make_pair(result, fold);
+}
+
+
+//-------------------------------------------------
+// overall_bounds - maximum bounds for all states
+//-------------------------------------------------
+
+render_bounds layout_element::component::overall_bounds() const
+{
+ return accumulate_bounds(m_bounds);
+}
+
+
+//-------------------------------------------------
+// bounds - bounds for a given state
+//-------------------------------------------------
+
+render_bounds layout_element::component::bounds(int state) const
+{
+ return interpolate_bounds(m_bounds, state);
+}
+
+
+//-------------------------------------------------
+// color - color for a given state
+//-------------------------------------------------
+
+render_color layout_element::component::color(int state) const
+{
+ return interpolate_color(m_color, state);
+}
+
+
+//-------------------------------------------------
+// preload - perform expensive operations upfront
+//-------------------------------------------------
+
+void layout_element::component::preload(running_machine &machine)
+{
+}
+
+
+//-------------------------------------------------
+// draw - draw element to texture for a given
+// state
+//-------------------------------------------------
+
+void layout_element::component::draw(running_machine &machine, bitmap_argb32 &dest, int state)
+{
+ // get the local scaled bounds
+ render_bounds const curbounds(bounds(state));
+ rectangle pixelbounds(
+ s32(curbounds.x0 * float(dest.width()) + 0.5F),
+ s32(floorf(curbounds.x1 * float(dest.width()) - 0.5F)),
+ s32(curbounds.y0 * float(dest.height()) + 0.5F),
+ s32(floorf(curbounds.y1 * float(dest.height()) - 0.5F)));
+
+ // based on the component type, add to the texture
+ if (!pixelbounds.empty())
+ draw_aligned(machine, dest, pixelbounds, state);
+}
+
+
+void layout_element::component::draw_aligned(running_machine &machine, bitmap_argb32 &dest, rectangle const &bounds, int state)
+{
+ // derived classes must override one form or other
+ throw false;
+}
+
+
+//-------------------------------------------------
+// maxstate - maximum state drawn differently
//-------------------------------------------------
-void layout_element::component::draw_text(render_font &font, bitmap_argb32 &dest, const rectangle &bounds, const char *str, int align)
+int layout_element::component::maxstate() const
{
- // compute premultiplied colors
- u32 r = color().r * 255.0f;
- u32 g = color().g * 255.0f;
- u32 b = color().b * 255.0f;
- u32 a = color().a * 255.0f;
+ return -1;
+}
+
+
+//-------------------------------------------------
+// draw_text - draw text in the specified color
+//-------------------------------------------------
+void layout_element::component::draw_text(
+ render_font &font,
+ bitmap_argb32 &dest,
+ const rectangle &bounds,
+ std::string_view str,
+ int align,
+ const render_color &color)
+{
// get the width of the string
float aspect = 1.0f;
s32 width;
@@ -2686,11 +3727,11 @@ void layout_element::component::draw_text(render_font &font, bitmap_argb32 &dest
width = font.string_width(bounds.height(), aspect, str);
if (width < bounds.width())
break;
- aspect *= 0.9f;
+ aspect *= 0.95f;
}
// get alignment
- s32 curx;
+ float curx;
switch (align)
{
// left
@@ -2705,7 +3746,7 @@ void layout_element::component::draw_text(render_font &font, bitmap_argb32 &dest
// default to center
default:
- curx = bounds.left() + (bounds.width() - width) / 2;
+ curx = bounds.left() + (bounds.width() - width) / 2.0f;
break;
}
@@ -2713,15 +3754,10 @@ void layout_element::component::draw_text(render_font &font, bitmap_argb32 &dest
bitmap_argb32 tempbitmap(dest.width(), dest.height());
// loop over characters
- const char *origs = str;
- const char *ends = origs + strlen(origs);
- const char *s = origs;
- char32_t schar;
-
- // loop over characters
- while (*s != 0)
+ while (!str.empty())
{
- int scharcount = uchar_from_utf8(&schar, s, ends - s);
+ char32_t schar;
+ int scharcount = uchar_from_utf8(&schar, str);
if (scharcount == -1)
break;
@@ -2736,22 +3772,17 @@ void layout_element::component::draw_text(render_font &font, bitmap_argb32 &dest
int effy = bounds.top() + y;
if (effy >= bounds.top() && effy <= bounds.bottom())
{
- u32 *src = &tempbitmap.pix32(y);
- u32 *d = &dest.pix32(effy);
+ u32 const *const src = &tempbitmap.pix(y);
+ u32 *const d = &dest.pix(effy);
for (int x = 0; x < chbounds.width(); x++)
{
- int effx = curx + x + chbounds.left();
+ int effx = int(curx) + x + chbounds.left();
if (effx >= bounds.left() && effx <= bounds.right())
{
u32 spix = rgb_t(src[x]).a();
if (spix != 0)
{
- rgb_t dpix = d[effx];
- u32 ta = (a * (spix + 1)) >> 8;
- u32 tr = (r * ta + dpix.r() * (0x100 - ta)) >> 8;
- u32 tg = (g * ta + dpix.g() * (0x100 - ta)) >> 8;
- u32 tb = (b * ta + dpix.b() * (0x100 - ta)) >> 8;
- d[effx] = rgb_t(tr, tg, tb);
+ alpha_blend(d[effx], color, spix / 255.0);
}
}
}
@@ -2760,7 +3791,7 @@ void layout_element::component::draw_text(render_font &font, bitmap_argb32 &dest
// advance in the X direction
curx += font.char_width(bounds.height(), aspect, schar);
- s += scharcount;
+ str.remove_prefix(scharcount);
}
}
@@ -2776,8 +3807,8 @@ void layout_element::component::draw_segment_horizontal_caps(bitmap_argb32 &dest
// loop over the width of the segment
for (int y = 0; y < width / 2; y++)
{
- u32 *d0 = &dest.pix32(midy - y);
- u32 *d1 = &dest.pix32(midy + y);
+ u32 *const d0 = &dest.pix(midy - y);
+ u32 *const d1 = &dest.pix(midy + y);
int ty = (y < width / 8) ? width / 8 : y;
// loop over the length of the segment
@@ -2809,8 +3840,8 @@ void layout_element::component::draw_segment_vertical_caps(bitmap_argb32 &dest,
// loop over the width of the segment
for (int x = 0; x < width / 2; x++)
{
- u32 *d0 = &dest.pix32(0, midx - x);
- u32 *d1 = &dest.pix32(0, midx + x);
+ u32 *const d0 = &dest.pix(0, midx - x);
+ u32 *const d1 = &dest.pix(0, midx + x);
int tx = (x < width / 8) ? width / 8 : x;
// loop over the length of the segment
@@ -2846,7 +3877,7 @@ void layout_element::component::draw_segment_diagonal_1(bitmap_argb32 &dest, int
for (int x = minx; x < maxx; x++)
if (x >= 0 && x < dest.width())
{
- u32 *d = &dest.pix32(0, x);
+ u32 *const d = &dest.pix(0, x);
int step = (x - minx) * ratio;
for (int y = maxy - width - step; y < maxy - step; y++)
@@ -2871,7 +3902,7 @@ void layout_element::component::draw_segment_diagonal_2(bitmap_argb32 &dest, int
for (int x = minx; x < maxx; x++)
if (x >= 0 && x < dest.width())
{
- u32 *d = &dest.pix32(0, x);
+ u32 *const d = &dest.pix(0, x);
int step = (x - minx) * ratio;
for (int y = miny + step; y < miny + step + width; y++)
@@ -2894,8 +3925,8 @@ void layout_element::component::draw_segment_decimal(bitmap_argb32 &dest, int mi
// iterate over y
for (u32 y = 0; y <= width; y++)
{
- u32 *d0 = &dest.pix32(midy - y);
- u32 *d1 = &dest.pix32(midy + y);
+ u32 *const d0 = &dest.pix(midy - y);
+ u32 *const d1 = &dest.pix(midy + y);
float xval = width * sqrt(1.0f - (float)(y * y) * ooradius2);
s32 left, right;
@@ -2923,7 +3954,7 @@ void layout_element::component::draw_segment_comma(bitmap_argb32 &dest, int minx
// draw line
for (int x = minx; x < maxx; x++)
{
- u32 *d = &dest.pix32(0, x);
+ u32 *const d = &dest.pix(0, x);
int step = (x - minx) * ratio;
for (int y = maxy; y < maxy - width - step; y--)
@@ -2940,7 +3971,7 @@ void layout_element::component::apply_skew(bitmap_argb32 &dest, int skewwidth)
{
for (int y = 0; y < dest.height(); y++)
{
- u32 *destrow = &dest.pix32(y);
+ u32 *const destrow = &dest.pix(y);
int offs = skewwidth * (dest.height() - y) / dest.height();
for (int x = dest.width() - skewwidth - 1; x >= 0; x--)
destrow[x + offs] = destrow[x];
@@ -2963,29 +3994,87 @@ struct layout_view::layer_lists { item_list backdrops, screens, overlays, bezels
//-------------------------------------------------
layout_view::layout_view(
- environment &env,
+ layout_environment &env,
util::xml::data_node const &viewnode,
element_map &elemmap,
group_map &groupmap)
- : m_name(make_name(env, viewnode))
- , m_aspect(1.0f)
- , m_scraspect(1.0f)
- , m_items()
+ : m_effaspect(1.0f)
+ , m_name(make_name(env, viewnode))
+ , m_unqualified_name(env.get_attribute_string(viewnode, "name"))
+ , m_elemmap(elemmap)
+ , m_defvismask(0U)
, m_has_art(false)
+ , m_show_ptr(false)
+ , m_ptr_time_out(true) // FIXME: add attribute for this
+ , m_exp_show_ptr(-1)
{
+ // check for explicit pointer display setting
+ if (viewnode.get_attribute_string_ptr("showpointers"))
+ {
+ m_show_ptr = env.get_attribute_bool(viewnode, "showpointers", false);
+ m_exp_show_ptr = m_show_ptr ? 1 : 0;
+ }
+
// parse the layout
m_expbounds.x0 = m_expbounds.y0 = m_expbounds.x1 = m_expbounds.y1 = 0;
- environment local(env);
+ view_environment local(env, m_name.c_str());
layer_lists layers;
local.set_parameter("viewname", std::string(m_name));
add_items(layers, local, viewnode, elemmap, groupmap, ROT0, identity_transform, render_color{ 1.0F, 1.0F, 1.0F, 1.0F }, true, false, true);
+ // can't support legacy layers and modern visibility toggles at the same time
+ if (!m_vistoggles.empty() && (!layers.backdrops.empty() || !layers.overlays.empty() || !layers.bezels.empty() || !layers.cpanels.empty() || !layers.marquees.empty()))
+ throw layout_syntax_error("view contains visibility toggles as well as legacy backdrop, overlay, bezel, cpanel and/or marquee elements");
+
+ // create visibility toggles for legacy layers
+ u32 mask(1U);
+ if (!layers.backdrops.empty())
+ {
+ m_vistoggles.emplace_back("Backdrops", mask);
+ for (item &backdrop : layers.backdrops)
+ backdrop.m_visibility_mask = mask;
+ m_defvismask |= mask;
+ mask <<= 1;
+ }
+ if (!layers.overlays.empty())
+ {
+ m_vistoggles.emplace_back("Overlays", mask);
+ for (item &overlay : layers.overlays)
+ overlay.m_visibility_mask = mask;
+ m_defvismask |= mask;
+ mask <<= 1;
+ }
+ if (!layers.bezels.empty())
+ {
+ m_vistoggles.emplace_back("Bezels", mask);
+ for (item &bezel : layers.bezels)
+ bezel.m_visibility_mask = mask;
+ m_defvismask |= mask;
+ mask <<= 1;
+ }
+ if (!layers.cpanels.empty())
+ {
+ m_vistoggles.emplace_back("Control Panels", mask);
+ for (item &cpanel : layers.cpanels)
+ cpanel.m_visibility_mask = mask;
+ m_defvismask |= mask;
+ mask <<= 1;
+ }
+ if (!layers.marquees.empty())
+ {
+ m_vistoggles.emplace_back("Backdrops", mask);
+ for (item &marquee : layers.marquees)
+ marquee.m_visibility_mask = mask;
+ m_defvismask |= mask;
+ mask <<= 1;
+ }
+
// deal with legacy element groupings
if (!layers.overlays.empty() || (layers.backdrops.size() <= 1))
{
// screens (-1) + overlays (RGB multiply) + backdrop (add) + bezels (alpha) + cpanels (alpha) + marquees (alpha)
for (item &backdrop : layers.backdrops)
- backdrop.set_blend_mode(BLENDMODE_ADD);
+ backdrop.m_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);
@@ -3000,7 +4089,7 @@ layout_view::layout_view(
for (item &screen : layers.screens)
{
if (screen.blend_mode() == -1)
- screen.set_blend_mode(BLENDMODE_ADD);
+ screen.m_blend_mode = BLENDMODE_ADD;
}
m_items.splice(m_items.end(), layers.backdrops);
m_items.splice(m_items.end(), layers.screens);
@@ -3009,8 +4098,18 @@ layout_view::layout_view(
m_items.splice(m_items.end(), layers.marquees);
}
+ // index items with keys supplied
+ for (item &curitem : m_items)
+ {
+ if (!curitem.id().empty())
+ {
+ if (!m_items_by_id.emplace(curitem.id(), curitem).second)
+ throw layout_syntax_error("view contains item with duplicate id attribute");
+ }
+ }
+
// calculate metrics
- recompute(render_layer_config());
+ recompute(default_visibility_mask(), false);
for (group_map::value_type &group : groupmap)
group.second.set_bounds_unresolved();
}
@@ -3026,86 +4125,299 @@ layout_view::~layout_view()
//-------------------------------------------------
+// get_item - get item by ID
+//-------------------------------------------------
+
+layout_view_item *layout_view::get_item(std::string const &id)
+{
+ auto const found(m_items_by_id.find(id));
+ return (m_items_by_id.end() != found) ? &found->second : nullptr;
+}
+
+
+//-------------------------------------------------
+// has_screen - return true if this view contains
+// the specified screen
+//-------------------------------------------------
+
+bool layout_view::has_screen(screen_device const &screen) const
+{
+ return std::find_if(m_items.begin(), m_items.end(), [&screen] (auto &itm) { return itm.screen() == &screen; }) != m_items.end();
+}
+
+
+//-------------------------------------------------
+// has_visible_screen - return true if this view
+// has the given screen visble
+//-------------------------------------------------
+
+bool layout_view::has_visible_screen(screen_device const &screen) const
+{
+ return std::find_if(m_screens.begin(), m_screens.end(), [&screen] (auto const &scr) { return &scr.get() == &screen; }) != m_screens.end();
+}
+
+
+//-------------------------------------------------
+// prepare_items - perform additional tasks
+// before rendering a frame
+//-------------------------------------------------
+
+void layout_view::prepare_items()
+{
+ if (!m_prepare_items.isnull())
+ m_prepare_items();
+
+ for (auto &[name, element] : m_elemmap)
+ element.prepare();
+}
+
+
+//-------------------------------------------------
// recompute - recompute the bounds and aspect
// ratio of a view and all of its contained items
//-------------------------------------------------
-void layout_view::recompute(render_layer_config layerconfig)
+void layout_view::recompute(u32 visibility_mask, bool zoom_to_screen)
{
- // reset the bounds
- m_bounds.x0 = m_bounds.y0 = m_bounds.x1 = m_bounds.y1 = 0.0f;
- m_scrbounds.x0 = m_scrbounds.y0 = m_scrbounds.x1 = m_scrbounds.y1 = 0.0f;
- m_screens.reset();
-
- // loop over all layers
+ // reset the bounds and collected active items
+ render_bounds scrbounds{ 0.0f, 0.0f, 0.0f, 0.0f };
+ m_bounds = scrbounds;
+ m_visible_items.clear();
+ m_screen_items.clear();
+ m_interactive_items.clear();
+ m_interactive_edges_x.clear();
+ m_interactive_edges_y.clear();
+ m_screens.clear();
+
+ // loop over items and filter by visibility mask
bool first = true;
bool scrfirst = true;
+ bool haveinput = false;
for (item &curitem : m_items)
{
- // 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 ((visibility_mask & curitem.visibility_mask()) == curitem.visibility_mask())
{
- if (scrfirst)
- m_scrbounds = curitem.m_rawbounds;
+ render_bounds const rawbounds = accumulate_bounds(curitem.m_rawbounds);
+
+ // accumulate bounds
+ m_visible_items.emplace_back(curitem);
+ if (first)
+ m_bounds = rawbounds;
else
- union_render_bounds(m_scrbounds, curitem.m_rawbounds);
- scrfirst = false;
+ m_bounds |= rawbounds;
+ first = false;
+
+ // accumulate visible screens and their bounds bounds
+ if (curitem.screen())
+ {
+ if (scrfirst)
+ scrbounds = rawbounds;
+ else
+ scrbounds |= rawbounds;
+ scrfirst = false;
+
+ // accumulate active screens
+ m_screen_items.emplace_back(curitem);
+ m_screens.emplace_back(*curitem.screen());
+ }
- // accumulate the screens in use while we're scanning
- m_screens.add(*curitem.m_screen);
+ // accumulate interactive elements
+ if (!curitem.clickthrough() || curitem.has_input())
+ m_interactive_items.emplace_back(curitem);
+ if (curitem.has_input())
+ haveinput = true;
}
}
+ // if show pointers isn't explicitly, update it based on visible items
+ if (0 > m_exp_show_ptr)
+ m_show_ptr = haveinput;
+
// if we have an explicit bounds, override it
if (m_expbounds.x1 > m_expbounds.x0)
m_bounds = m_expbounds;
- // if we're handling things normally, the target bounds are (0,0)-(1,1)
render_bounds target_bounds;
- if (!layerconfig.zoom_to_screen() || m_screens.count() == 0)
+ if (!zoom_to_screen || scrfirst)
{
- // compute the aspect ratio of the view
- m_aspect = (m_bounds.x1 - m_bounds.x0) / (m_bounds.y1 - m_bounds.y0);
-
+ // if we're handling things normally, the target bounds are (0,0)-(1,1)
+ m_effaspect = ((m_bounds.x1 > m_bounds.x0) && (m_bounds.y1 > m_bounds.y0)) ? m_bounds.aspect() : 1.0f;
target_bounds.x0 = target_bounds.y0 = 0.0f;
target_bounds.x1 = target_bounds.y1 = 1.0f;
}
-
- // if we're cropping, we want the screen area to fill (0,0)-(1,1)
else
{
- // compute the aspect ratio of the screen
- m_scraspect = (m_scrbounds.x1 - m_scrbounds.x0) / (m_scrbounds.y1 - m_scrbounds.y0);
-
- float targwidth = (m_bounds.x1 - m_bounds.x0) / (m_scrbounds.x1 - m_scrbounds.x0);
- float targheight = (m_bounds.y1 - m_bounds.y0) / (m_scrbounds.y1 - m_scrbounds.y0);
- target_bounds.x0 = (m_bounds.x0 - m_scrbounds.x0) / (m_bounds.x1 - m_bounds.x0) * targwidth;
- target_bounds.y0 = (m_bounds.y0 - m_scrbounds.y0) / (m_bounds.y1 - m_bounds.y0) * targheight;
- target_bounds.x1 = target_bounds.x0 + targwidth;
- target_bounds.y1 = target_bounds.y0 + targheight;
+ // if we're cropping, we want the screen area to fill (0,0)-(1,1)
+ m_effaspect = ((scrbounds.x1 > scrbounds.x0) && (scrbounds.y1 > scrbounds.y0)) ? scrbounds.aspect() : 1.0f;
+ target_bounds.x0 = (m_bounds.x0 - scrbounds.x0) / scrbounds.width();
+ target_bounds.y0 = (m_bounds.y0 - scrbounds.y0) / scrbounds.height();
+ target_bounds.x1 = target_bounds.x0 + (m_bounds.width() / scrbounds.width());
+ target_bounds.y1 = target_bounds.y0 + (m_bounds.height() / scrbounds.height());
}
// determine the scale/offset for normalization
- float xoffs = m_bounds.x0;
- float yoffs = m_bounds.y0;
- float xscale = (target_bounds.x1 - target_bounds.x0) / (m_bounds.x1 - m_bounds.x0);
- float yscale = (target_bounds.y1 - target_bounds.y0) / (m_bounds.y1 - m_bounds.y0);
+ float const xoffs = m_bounds.x0;
+ float const yoffs = m_bounds.y0;
+ float const xscale = target_bounds.width() / m_bounds.width();
+ float const yscale = target_bounds.height() / m_bounds.height();
// normalize all the item bounds
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;
+ assert(curitem.m_rawbounds.size() == curitem.m_bounds.size());
+ std::copy(curitem.m_rawbounds.begin(), curitem.m_rawbounds.end(), curitem.m_bounds.begin());
+ normalize_bounds(curitem.m_bounds, target_bounds.x0, target_bounds.y0, xoffs, yoffs, xscale, yscale);
+ }
+
+ // sort edges of interactive items
+ LOGMASKED(LOG_INTERACTIVE_ITEMS, "Recalculated view '%s' with %u interactive items\n",
+ name(), m_interactive_items.size());
+ //std::reverse(m_interactive_items.begin(), m_interactive_items.end()); TODO: flip hit test order to match visual order
+ m_interactive_edges_x.reserve(m_interactive_items.size() * 2);
+ m_interactive_edges_y.reserve(m_interactive_items.size() * 2);
+ for (unsigned i = 0; m_interactive_items.size() > i; ++i)
+ {
+ item &curitem(m_interactive_items[i]);
+ render_bounds const curbounds(accumulate_bounds(curitem.m_bounds));
+ LOGMASKED(LOG_INTERACTIVE_ITEMS, "%u: (%s %s %s %s) hasinput=%s clickthrough=%s\n",
+ i, curbounds.x0, curbounds.y0, curbounds.x1, curbounds.y1, curitem.has_input(), curitem.clickthrough());
+ m_interactive_edges_x.emplace_back(i, curbounds.x0, false);
+ m_interactive_edges_x.emplace_back(i, curbounds.x1, true);
+ m_interactive_edges_y.emplace_back(i, curbounds.y0, false);
+ m_interactive_edges_y.emplace_back(i, curbounds.y1, true);
+ }
+ std::sort(m_interactive_edges_x.begin(), m_interactive_edges_x.end());
+ std::sort(m_interactive_edges_y.begin(), m_interactive_edges_y.end());
+
+ if (VERBOSE & LOG_INTERACTIVE_ITEMS)
+ {
+ for (edge const &e : m_interactive_edges_x)
+ LOGMASKED(LOG_INTERACTIVE_ITEMS, "x=%s %c%u\n", e.position(), e.trailing() ? ']' : '[', e.index());
+ for (edge const &e : m_interactive_edges_y)
+ LOGMASKED(LOG_INTERACTIVE_ITEMS, "y=%s %c%u\n", e.position(), e.trailing() ? ']' : '[', e.index());
}
+
+ // additional actions typically supplied by script
+ if (!m_recomputed.isnull())
+ m_recomputed();
+}
+
+
+//-------------------------------------------------
+// set_show_pointers - set whether pointers
+// should be displayed
+//-------------------------------------------------
+
+void layout_view::set_show_pointers(bool value) noexcept
+{
+ m_show_ptr = value;
+ m_exp_show_ptr = value ? 1 : 0;
+}
+
+
+//-------------------------------------------------
+// set_pointers_time_out - set whether pointers
+// should be hidden after inactivity
+//-------------------------------------------------
+
+void layout_view::set_hide_inactive_pointers(bool value) noexcept
+{
+ m_ptr_time_out = value;
+}
+
+
+//-------------------------------------------------
+// set_prepare_items_callback - set handler called
+// before adding items to render target
+//-------------------------------------------------
+
+void layout_view::set_prepare_items_callback(prepare_items_delegate &&handler)
+{
+ m_prepare_items = std::move(handler);
+}
+
+
+//-------------------------------------------------
+// set_preload_callback - set handler called
+// after preloading elements
+//-------------------------------------------------
+
+void layout_view::set_preload_callback(preload_delegate &&handler)
+{
+ m_preload = std::move(handler);
+}
+
+
+//-------------------------------------------------
+// set_recomputed_callback - set handler called
+// after recomputing item bounds
+//-------------------------------------------------
+
+void layout_view::set_recomputed_callback(recomputed_delegate &&handler)
+{
+ m_recomputed = std::move(handler);
+}
+
+
+//-------------------------------------------------
+// set_pointer_updated_callback - set handler
+// called for pointer input
+//-------------------------------------------------
+
+void layout_view::set_pointer_updated_callback(pointer_updated_delegate &&handler)
+{
+ m_pointer_updated = std::move(handler);
+}
+
+
+//-------------------------------------------------
+// set_pointer_left_callback - set handler for
+// pointer leaving normally
+//-------------------------------------------------
+
+void layout_view::set_pointer_left_callback(pointer_left_delegate &&handler)
+{
+ m_pointer_left = std::move(handler);
+}
+
+
+//-------------------------------------------------
+// set_pointer_aborted_callback - set handler for
+// pointer leaving abnormally
+//-------------------------------------------------
+
+void layout_view::set_pointer_aborted_callback(pointer_left_delegate &&handler)
+{
+ m_pointer_aborted = std::move(handler);
+}
+
+
+//-------------------------------------------------
+// set_forget_pointers_callback - set handler for
+// abandoning pointer input
+//-------------------------------------------------
+
+void layout_view::set_forget_pointers_callback(forget_pointers_delegate &&handler)
+{
+ m_forget_pointers = std::move(handler);
+}
+
+
+//-------------------------------------------------
+// preload - perform expensive loading upfront
+// for visible elements
+//-------------------------------------------------
+
+void layout_view::preload()
+{
+ for (item &curitem : m_visible_items)
+ {
+ if (curitem.element())
+ curitem.element()->preload();
+ }
+
+ if (!m_preload.isnull())
+ m_preload();
}
@@ -3126,7 +4438,7 @@ void layout_view::resolve_tags()
void layout_view::add_items(
layer_lists &layers,
- environment &env,
+ view_environment &env,
util::xml::data_node const &parentnode,
element_map &elemmap,
group_map &groupmap,
@@ -3161,11 +4473,6 @@ void layout_view::add_items(
else
env.set_repeat_parameter(*itemnode, init);
}
- else if (!strcmp(itemnode->get_name(), "backdrop"))
- {
- layers.backdrops.emplace_back(env, *itemnode, elemmap, orientation, trans, color);
- m_has_art = true;
- }
else if (!strcmp(itemnode->get_name(), "screen"))
{
layers.screens.emplace_back(env, *itemnode, elemmap, orientation, trans, color);
@@ -3175,31 +4482,46 @@ void layout_view::add_items(
layers.screens.emplace_back(env, *itemnode, elemmap, orientation, trans, color);
m_has_art = true;
}
+ else if (!strcmp(itemnode->get_name(), "backdrop"))
+ {
+ if (layers.backdrops.empty())
+ osd_printf_warning("Warning: layout view '%s' contains deprecated backdrop element\n", name());
+ layers.backdrops.emplace_back(env, *itemnode, elemmap, orientation, trans, color);
+ m_has_art = true;
+ }
else if (!strcmp(itemnode->get_name(), "overlay"))
{
+ if (layers.overlays.empty())
+ osd_printf_warning("Warning: layout view '%s' contains deprecated overlay element\n", name());
layers.overlays.emplace_back(env, *itemnode, elemmap, orientation, trans, color);
m_has_art = true;
}
else if (!strcmp(itemnode->get_name(), "bezel"))
{
+ if (layers.bezels.empty())
+ osd_printf_warning("Warning: layout view '%s' contains deprecated bezel element\n", name());
layers.bezels.emplace_back(env, *itemnode, elemmap, orientation, trans, color);
m_has_art = true;
}
else if (!strcmp(itemnode->get_name(), "cpanel"))
{
+ if (layers.cpanels.empty())
+ osd_printf_warning("Warning: layout view '%s' contains deprecated cpanel element\n", name());
layers.cpanels.emplace_back(env, *itemnode, elemmap, orientation, trans, color);
m_has_art = true;
}
else if (!strcmp(itemnode->get_name(), "marquee"))
{
+ if (layers.marquees.empty())
+ osd_printf_warning("Warning: layout view '%s' contains deprecated marquee element\n", name());
layers.marquees.emplace_back(env, *itemnode, elemmap, orientation, trans, color);
m_has_art = true;
}
else if (!strcmp(itemnode->get_name(), "group"))
{
- char const *ref(env.get_attribute_string(*itemnode, "ref", nullptr));
- if (!ref)
- throw layout_syntax_error("nested group must have ref attribute");
+ std::string const ref(env.get_attribute_string(*itemnode, "ref"));
+ if (ref.empty())
+ throw layout_syntax_error("group instantiation must have non-empty ref attribute");
group_map::iterator const found(groupmap.find(ref));
if (groupmap.end() == found)
@@ -3222,7 +4544,7 @@ void layout_view::add_items(
grouptrans = found->second.make_transform(grouporient, trans);
}
- environment local(env);
+ view_environment local(env, false);
add_items(
layers,
local,
@@ -3231,7 +4553,7 @@ void layout_view::add_items(
groupmap,
orientation_add(grouporient, orientation),
grouptrans,
- render_color_multiply(env.parse_color(itemnode->get_child("color")), color),
+ env.parse_color(itemnode->get_child("color")) * color,
false,
false,
true);
@@ -3241,13 +4563,28 @@ void layout_view::add_items(
int const count(env.get_attribute_int(*itemnode, "count", -1));
if (0 >= count)
throw layout_syntax_error("repeat must have positive integer count attribute");
- environment local(env);
+ view_environment local(env, false);
for (int i = 0; count > i; ++i)
{
add_items(layers, local, *itemnode, elemmap, groupmap, orientation, trans, color, false, true, !i);
local.increment_parameters();
}
}
+ else if (!strcmp(itemnode->get_name(), "collection"))
+ {
+ std::string_view const name(env.get_attribute_string(*itemnode, "name"));
+ if (name.empty())
+ throw layout_syntax_error("collection must have non-empty name attribute");
+
+ auto const found(std::find_if(m_vistoggles.begin(), m_vistoggles.end(), [name] (auto const &x) { return x.name() == name; }));
+ if (m_vistoggles.end() != found)
+ throw layout_syntax_error(util::string_format("duplicate collection name '%s'", name));
+
+ m_defvismask |= u32(env.get_attribute_bool(*itemnode, "visible", true) ? 1 : 0) << m_vistoggles.size(); // TODO: make this less hacky
+ view_environment local(env, true);
+ m_vistoggles.emplace_back(std::string(name), local.visibility_mask());
+ add_items(layers, local, *itemnode, elemmap, groupmap, orientation, trans, color, false, false, true);
+ }
else
{
throw layout_syntax_error(util::string_format("unknown view item %s", itemnode->get_name()));
@@ -3261,15 +4598,15 @@ void layout_view::add_items(
}
}
-std::string layout_view::make_name(environment &env, util::xml::data_node const &viewnode)
+std::string layout_view::make_name(layout_environment &env, util::xml::data_node const &viewnode)
{
- char const *const name(env.get_attribute_string(viewnode, "name", nullptr));
- if (!name)
- throw layout_syntax_error("view must have name attribute");
+ std::string_view const name(env.get_attribute_string(viewnode, "name"));
+ if (name.empty())
+ throw layout_syntax_error("view must have non-empty name attribute");
if (env.is_root_device())
{
- return name;
+ return std::string(name);
}
else
{
@@ -3287,50 +4624,75 @@ std::string layout_view::make_name(environment &env, util::xml::data_node const
//**************************************************************************
//-------------------------------------------------
-// item - constructor
+// layout_view_item - constructor
//-------------------------------------------------
-layout_view::item::item(
- environment &env,
+layout_view_item::layout_view_item(
+ view_environment &env,
util::xml::data_node const &itemnode,
element_map &elemmap,
int orientation,
layout_group::transform const &trans,
render_color const &color)
: 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(make_input_tag(env, itemnode))
+ , m_output(env.device(), std::string(env.get_attribute_string(itemnode, "name")))
+ , m_animoutput(env.device(), make_child_output_tag(env, itemnode, "animate"))
+ , m_scrollxoutput(env.device(), make_child_output_tag(env, itemnode, "xscroll"))
+ , m_scrollyoutput(env.device(), make_child_output_tag(env, itemnode, "yscroll"))
+ , m_animinput_port(nullptr)
+ , m_scrollxinput_port(nullptr)
+ , m_scrollyinput_port(nullptr)
+ , m_scrollwrapx(make_child_wrap(env, itemnode, "xscroll"))
+ , m_scrollwrapy(make_child_wrap(env, itemnode, "yscroll"))
+ , m_elem_state(m_element ? m_element->default_state() : 0)
+ , m_scrollsizex(make_child_size(env, itemnode, "xscroll"))
+ , m_scrollsizey(make_child_size(env, itemnode, "yscroll"))
+ , m_scrollposx(0.0f)
+ , m_scrollposy(0.0f)
+ , m_animmask(make_child_mask(env, itemnode, "animate"))
+ , m_scrollxmask(make_child_mask(env, itemnode, "xscroll"))
+ , m_scrollymask(make_child_mask(env, itemnode, "yscroll"))
+ , m_scrollxmin(make_child_min(env, itemnode, "xscroll"))
+ , m_scrollymin(make_child_min(env, itemnode, "yscroll"))
+ , m_scrollxmax(make_child_max(env, itemnode, "xscroll", m_scrollxmask))
+ , m_scrollymax(make_child_max(env, itemnode, "yscroll", m_scrollymask))
+ , m_animshift(get_state_shift(m_animmask))
+ , m_scrollxshift(get_state_shift(m_scrollxmask))
+ , m_scrollyshift(get_state_shift(m_scrollymask))
, m_input_port(nullptr)
, m_input_field(nullptr)
, m_input_mask(env.get_attribute_int(itemnode, "inputmask", 0))
- , m_input_shift(0)
- , m_input_raw(0 != env.get_attribute_int(itemnode, "inputraw", 0))
+ , m_input_shift(get_state_shift(m_input_mask))
+ , m_clickthrough(env.get_attribute_bool(itemnode, "clickthrough", "yes"))
, 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_color(make_color(env, itemnode, color))
, m_blend_mode(get_blend_mode(env, itemnode))
+ , m_visibility_mask(env.visibility_mask())
+ , m_id(env.get_attribute_string(itemnode, "id"))
+ , m_input_tag(make_input_tag(env, itemnode))
+ , m_animinput_tag(make_child_input_tag(env, itemnode, "animate"))
+ , m_scrollxinput_tag(make_child_input_tag(env, itemnode, "xscroll"))
+ , m_scrollyinput_tag(make_child_input_tag(env, itemnode, "yscroll"))
+ , m_rawbounds(make_bounds(env, itemnode, trans))
+ , m_have_output(!env.get_attribute_string(itemnode, "name").empty())
+ , m_input_raw(env.get_attribute_bool(itemnode, "inputraw", 0))
+ , m_have_animoutput(!make_child_output_tag(env, itemnode, "animate").empty())
+ , m_have_scrollxoutput(!make_child_output_tag(env, itemnode, "xscroll").empty())
+ , m_have_scrollyoutput(!make_child_output_tag(env, itemnode, "yscroll").empty())
+ , m_has_clickthrough(!env.get_attribute_string(itemnode, "clickthrough").empty())
{
- // outputs need resolving
- if (m_have_output)
- m_output.resolve();
-
// fetch common data
int index = env.get_attribute_int(itemnode, "index", -1);
if (index != -1)
- m_screen = screen_device_iterator(env.machine().root_device()).byindex(index);
- 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();
+ m_screen = screen_device_enumerator(env.machine().root_device()).byindex(index);
// sanity checks
if (strcmp(itemnode.get_name(), "screen") == 0)
{
if (itemnode.has_attribute("tag"))
{
- char const *const tag(env.get_attribute_string(itemnode, "tag", ""));
+ std::string_view const tag(env.get_attribute_string(itemnode, "tag"));
m_screen = dynamic_cast<screen_device *>(env.device().subdevice(tag));
if (!m_screen)
throw layout_reference_error(util::string_format("invalid screen tag '%d'", tag));
@@ -3342,78 +4704,182 @@ layout_view::item::item(
}
else if (!m_element)
{
- throw layout_syntax_error(util::string_format("item of type %s require an element tag", itemnode.get_name()));
+ throw layout_syntax_error(util::string_format("item of type %s requires an element tag", itemnode.get_name()));
+ }
+ else if (m_scrollxmin == m_scrollxmax)
+ {
+ throw layout_syntax_error(util::string_format("item X scroll minimum and maximum both equal to %u", m_scrollxmin));
}
+ else if (m_scrollymin == m_scrollymax)
+ {
+ throw layout_syntax_error(util::string_format("item Y scroll minimum and maximum both equal to %u", m_scrollymin));
+ }
+
+ // this can be called before resolving tags, make it return something valid
+ m_bounds = m_rawbounds;
+ m_get_bounds = bounds_delegate(&emu::render::detail::bounds_step::get, &m_bounds.front());
}
//-------------------------------------------------
-// item - destructor
+// layout_view_item - destructor
//-------------------------------------------------
-layout_view::item::~item()
+layout_view_item::~layout_view_item()
{
}
//-------------------------------------------------
-// screen_container - retrieve screen container
+// set_element_state_callback - set callback to
+// obtain element state value
//-------------------------------------------------
+void layout_view_item::set_element_state_callback(state_delegate &&handler)
+{
+ if (!handler.isnull())
+ m_get_elem_state = std::move(handler);
+ else
+ m_get_elem_state = default_get_elem_state();
+}
+
+
+//-------------------------------------------------
+// set_animation_state_callback - set callback to
+// obtain animation state
+//-------------------------------------------------
-render_container *layout_view::item::screen_container(running_machine &machine) const
+void layout_view_item::set_animation_state_callback(state_delegate &&handler)
{
- return (m_screen != nullptr) ? &m_screen->container() : nullptr;
+ if (!handler.isnull())
+ m_get_anim_state = std::move(handler);
+ else
+ m_get_anim_state = default_get_anim_state();
}
//-------------------------------------------------
-// state - fetch state based on configured source
+// set_bounds_callback - set callback to obtain
+// bounds
//-------------------------------------------------
-int layout_view::item::state() const
+void layout_view_item::set_bounds_callback(bounds_delegate &&handler)
{
- assert(m_element);
+ if (!handler.isnull())
+ m_get_bounds = std::move(handler);
+ else
+ m_get_bounds = default_get_bounds();
+}
- if (m_have_output)
- {
- // if configured to track an output, fetch its value
- return m_output;
- }
- else if (!m_input_tag.empty())
- {
- // if configured to an input, fetch the input value
- if (m_input_port)
- {
- if (m_input_raw)
- {
- return (m_input_port->read() & m_input_mask) >> m_input_shift;
- }
- else
- {
- ioport_field const *const field(m_input_field ? m_input_field : m_input_port->field(m_input_mask));
- if (field)
- return ((m_input_port->read() ^ field->defvalue()) & m_input_mask) ? 1 : 0;
- }
- }
- }
- return 0;
+//-------------------------------------------------
+// set_color_callback - set callback to obtain
+// color
+//-------------------------------------------------
+
+void layout_view_item::set_color_callback(color_delegate &&handler)
+{
+ if (!handler.isnull())
+ m_get_color = std::move(handler);
+ else
+ m_get_color = default_get_color();
+}
+
+
+//-------------------------------------------------
+// set_scroll_size_x_callback - set callback to
+// obtain horizontal scroll window size
+//-------------------------------------------------
+
+void layout_view_item::set_scroll_size_x_callback(scroll_size_delegate &&handler)
+{
+ if (!handler.isnull())
+ m_get_scroll_size_x = std::move(handler);
+ else
+ m_get_scroll_size_x = default_get_scroll_size_x();
+}
+
+
+//-------------------------------------------------
+// set_scroll_size_y_callback - set callback to
+// obtain vertical scroll window size
+//-------------------------------------------------
+
+void layout_view_item::set_scroll_size_y_callback(scroll_size_delegate &&handler)
+{
+ if (!handler.isnull())
+ m_get_scroll_size_y = std::move(handler);
+ else
+ m_get_scroll_size_y = default_get_scroll_size_y();
+}
+
+
+//-------------------------------------------------
+// set_scroll_pos_x_callback - set callback to
+// obtain horizontal scroll position
+//-------------------------------------------------
+
+void layout_view_item::set_scroll_pos_x_callback(scroll_pos_delegate &&handler)
+{
+ if (!handler.isnull())
+ m_get_scroll_pos_x = std::move(handler);
+ else
+ m_get_scroll_pos_x = default_get_scroll_pos_x();
+}
+
+
+//-------------------------------------------------
+// set_scroll_pos_y_callback - set callback to
+// obtain vertical scroll position
+//-------------------------------------------------
+
+void layout_view_item::set_scroll_pos_y_callback(scroll_pos_delegate &&handler)
+{
+ if (!handler.isnull())
+ m_get_scroll_pos_y = std::move(handler);
+ else
+ m_get_scroll_pos_y = default_get_scroll_pos_y();
}
-//---------------------------------------------
+//-------------------------------------------------
// resolve_tags - resolve tags, if any are set
-//---------------------------------------------
+//-------------------------------------------------
-void layout_view::item::resolve_tags()
+void layout_view_item::resolve_tags()
{
+ // resolve element state output and set default value
+ if (m_have_output)
+ {
+ m_output.resolve();
+ if (m_element)
+ m_output = m_element->default_state();
+ }
+
+ // resolve animation state and scroll outputs
+ if (m_have_animoutput)
+ m_animoutput.resolve();
+ if (m_have_scrollxoutput)
+ m_scrollxoutput.resolve();
+ if (m_have_scrollyoutput)
+ m_scrollyoutput.resolve();
+
+ // resolve animation state and scroll inputs
+ if (!m_animinput_tag.empty())
+ m_animinput_port = m_element->machine().root_device().ioport(m_animinput_tag);
+ if (!m_scrollxinput_tag.empty())
+ m_scrollxinput_port = m_element->machine().root_device().ioport(m_scrollxinput_tag);
+ if (!m_scrollyinput_tag.empty())
+ m_scrollyinput_port = m_element->machine().root_device().ioport(m_scrollyinput_tag);
+
+ // resolve element state input
if (!m_input_tag.empty())
{
- m_input_port = m_element->machine().root_device().ioport(m_input_tag.c_str());
+ m_input_port = m_element->machine().root_device().ioport(m_input_tag);
if (m_input_port)
{
- for (ioport_field &field : m_input_port->fields())
+ // if there's a matching unconditional field, cache it
+ for (ioport_field const &field : m_input_port->fields())
{
if (field.mask() & m_input_mask)
{
@@ -3422,19 +4888,351 @@ void layout_view::item::resolve_tags()
break;
}
}
+
+ // if clickthrough isn't explicitly configured, having an I/O port implies false
+ if (!m_has_clickthrough)
+ m_clickthrough = false;
}
}
+
+ // choose optimal handlers
+ m_get_elem_state = default_get_elem_state();
+ m_get_anim_state = default_get_anim_state();
+ m_get_bounds = default_get_bounds();
+ m_get_color = default_get_color();
+ m_get_scroll_size_x = default_get_scroll_size_x();
+ m_get_scroll_size_y = default_get_scroll_size_y();
+ m_get_scroll_pos_x = default_get_scroll_pos_x();
+ m_get_scroll_pos_y = default_get_scroll_pos_y();
+}
+
+
+//-------------------------------------------------
+// default_get_elem_state - get default element
+// state handler
+//-------------------------------------------------
+
+layout_view_item::state_delegate layout_view_item::default_get_elem_state()
+{
+ if (m_have_output)
+ return state_delegate(&layout_view_item::get_output, this);
+ else if (!m_input_port)
+ return state_delegate(&layout_view_item::get_state, this);
+ else if (m_input_raw)
+ return state_delegate(&layout_view_item::get_input_raw, this);
+ else if (m_input_field)
+ return state_delegate(&layout_view_item::get_input_field_cached, this);
+ else
+ return state_delegate(&layout_view_item::get_input_field_conditional, this);
+}
+
+
+//-------------------------------------------------
+// default_get_anim_state - get default animation
+// state handler
+//-------------------------------------------------
+
+layout_view_item::state_delegate layout_view_item::default_get_anim_state()
+{
+ if (m_have_animoutput)
+ return state_delegate(&layout_view_item::get_anim_output, this);
+ else if (m_animinput_port)
+ return state_delegate(&layout_view_item::get_anim_input, this);
+ else
+ return default_get_elem_state();
+}
+
+
+//-------------------------------------------------
+// default_get_bounds - get default bounds handler
+//-------------------------------------------------
+
+layout_view_item::bounds_delegate layout_view_item::default_get_bounds()
+{
+ return (m_bounds.size() == 1U)
+ ? bounds_delegate(&emu::render::detail::bounds_step::get, &m_bounds.front())
+ : bounds_delegate(&layout_view_item::get_interpolated_bounds, this);
+}
+
+
+//-------------------------------------------------
+// default_get_color - get default color handler
+//-------------------------------------------------
+
+layout_view_item::color_delegate layout_view_item::default_get_color()
+{
+ return (m_color.size() == 1U)
+ ? color_delegate(&emu::render::detail::color_step::get, &const_cast<emu::render::detail::color_step &>(m_color.front()))
+ : color_delegate(&layout_view_item::get_interpolated_color, this);
+}
+
+
+//-------------------------------------------------
+// default_get_scroll_size_x - get default
+// horizontal scroll window size handler
+//-------------------------------------------------
+
+layout_view_item::scroll_size_delegate layout_view_item::default_get_scroll_size_x()
+{
+ return scroll_size_delegate(&layout_view_item::get_scrollsizex, this);
+}
+
+
+//-------------------------------------------------
+// default_get_scroll_size_y - get default
+// vertical scroll window size handler
+//-------------------------------------------------
+
+layout_view_item::scroll_size_delegate layout_view_item::default_get_scroll_size_y()
+{
+ return scroll_size_delegate(&layout_view_item::get_scrollsizey, this);
+}
+
+
+//-------------------------------------------------
+// default_get_scroll_pos_x - get default
+// horizontal scroll position handler
+//-------------------------------------------------
+
+layout_view_item::scroll_pos_delegate layout_view_item::default_get_scroll_pos_x()
+{
+ if (m_have_scrollxoutput)
+ return scroll_pos_delegate(m_scrollwrapx ? &layout_view_item::get_scrollx_output<true> : &layout_view_item::get_scrollx_output<false>, this);
+ else if (m_scrollxinput_port)
+ return scroll_pos_delegate(m_scrollwrapx ? &layout_view_item::get_scrollx_input<true> : &layout_view_item::get_scrollx_input<false>, this);
+ else
+ return scroll_pos_delegate(&layout_view_item::get_scrollposx, this);
+}
+
+
+//-------------------------------------------------
+// default_get_scroll_pos_y - get default
+// vertical scroll position handler
+//-------------------------------------------------
+
+layout_view_item::scroll_pos_delegate layout_view_item::default_get_scroll_pos_y()
+{
+ if (m_have_scrollyoutput)
+ return scroll_pos_delegate(m_scrollwrapy ? &layout_view_item::get_scrolly_output<true> : &layout_view_item::get_scrolly_output<false>, this);
+ else if (m_scrollyinput_port)
+ return scroll_pos_delegate(m_scrollwrapy ? &layout_view_item::get_scrolly_input<true> : &layout_view_item::get_scrolly_input<false>, this);
+ else
+ return scroll_pos_delegate(&layout_view_item::get_scrollposy, this);
+}
+
+
+//-------------------------------------------------
+// get_state - get state when no bindings
+//-------------------------------------------------
+
+int layout_view_item::get_state() const
+{
+ return m_elem_state;
}
-//---------------------------------------------
+//-------------------------------------------------
+// get_output - get element state output
+//-------------------------------------------------
+
+int layout_view_item::get_output() const
+{
+ assert(m_have_output);
+ return int(s32(m_output));
+}
+
+
+//-------------------------------------------------
+// get_input_raw - get element state input
+//-------------------------------------------------
+
+int layout_view_item::get_input_raw() const
+{
+ assert(m_input_port);
+ return int(std::make_signed_t<ioport_value>((m_input_port->read() & m_input_mask) >> m_input_shift));
+}
+
+
+//-------------------------------------------------
+// get_input_field_cached - element state
+//-------------------------------------------------
+
+int layout_view_item::get_input_field_cached() const
+{
+ assert(m_input_port);
+ assert(m_input_field);
+ return ((m_input_port->read() ^ m_input_field->defvalue()) & m_input_mask) ? 1 : 0;
+}
+
+
+//-------------------------------------------------
+// get_input_field_conditional - element state
+//-------------------------------------------------
+
+int layout_view_item::get_input_field_conditional() const
+{
+ assert(m_input_port);
+ assert(!m_input_field);
+ ioport_field const *const field(m_input_port->field(m_input_mask));
+ return (field && ((m_input_port->read() ^ field->defvalue()) & m_input_mask)) ? 1 : 0;
+}
+
+
+//-------------------------------------------------
+// get_anim_output - get animation output
+//-------------------------------------------------
+
+int layout_view_item::get_anim_output() const
+{
+ assert(m_have_animoutput);
+ return int(unsigned((u32(s32(m_animoutput) & m_animmask) >> m_animshift)));
+}
+
+
+//-------------------------------------------------
+// get_anim_input - get animation input
+//-------------------------------------------------
+
+int layout_view_item::get_anim_input() const
+{
+ assert(m_animinput_port);
+ return int(std::make_signed_t<ioport_value>((m_animinput_port->read() & m_animmask) >> m_animshift));
+}
+
+
+//-------------------------------------------------
+// get_scrollsizex - get horizontal scroll window
+// size
+//-------------------------------------------------
+
+float layout_view_item::get_scrollsizex() const
+{
+ return m_scrollsizex;
+}
+
+
+//-------------------------------------------------
+// get_scrollsizey - get vertical scroll window
+// size
+//-------------------------------------------------
+
+float layout_view_item::get_scrollsizey() const
+{
+ return m_scrollsizey;
+}
+
+
+//-------------------------------------------------
+// get_scrollposx - get horizontal scroll
+// position
+//-------------------------------------------------
+
+float layout_view_item::get_scrollposx() const
+{
+ return m_scrollposx;
+}
+
+
+//-------------------------------------------------
+// get_scrollposy - get vertical scroll position
+//-------------------------------------------------
+
+float layout_view_item::get_scrollposy() const
+{
+ return m_scrollposy;
+}
+
+
+//-------------------------------------------------
+// get_scrollx_output - get scaled horizontal
+// scroll output
+//-------------------------------------------------
+
+template <bool Wrap>
+float layout_view_item::get_scrollx_output() const
+{
+ assert(m_have_scrollxoutput);
+ u32 const unscaled(((u32(s32(m_scrollxoutput)) & m_scrollxmask) >> m_scrollxshift) - m_scrollxmin);
+ float const range(std::make_signed_t<ioport_value>(m_scrollxmax - m_scrollxmin) + (!Wrap ? 0 : (m_scrollxmin < m_scrollxmax) ? 1 : -1));
+ return float(s32(unscaled)) / range;
+}
+
+
+//-------------------------------------------------
+// get_scrollx_input - get scaled horizontal
+// scroll input
+//-------------------------------------------------
+
+template <bool Wrap>
+float layout_view_item::get_scrollx_input() const
+{
+ assert(m_scrollxinput_port);
+ ioport_value const unscaled(((m_scrollxinput_port->read() & m_scrollxmask) >> m_scrollxshift) - m_scrollxmin);
+ float const range(std::make_signed_t<ioport_value>(m_scrollxmax - m_scrollxmin) + (!Wrap ? 0 : (m_scrollxmin < m_scrollxmax) ? 1 : -1));
+ return float(std::make_signed_t<ioport_value>(unscaled)) / range;
+}
+
+
+//-------------------------------------------------
+// get_scrolly_output - get scaled vertical
+// scroll output
+//-------------------------------------------------
+
+template <bool Wrap>
+float layout_view_item::get_scrolly_output() const
+{
+ assert(m_have_scrollyoutput);
+ u32 const unscaled(((u32(s32(m_scrollyoutput)) & m_scrollymask) >> m_scrollyshift) - m_scrollymin);
+ float const range(std::make_signed_t<ioport_value>(m_scrollymax - m_scrollymin) + (!Wrap ? 0 : (m_scrollymin < m_scrollymax) ? 1 : -1));
+ return float(s32(unscaled)) / range;
+}
+
+
+//-------------------------------------------------
+// get_scrolly_input - get scaled vertical scroll
+// input
+//-------------------------------------------------
+
+template <bool Wrap>
+float layout_view_item::get_scrolly_input() const
+{
+ assert(m_scrollyinput_port);
+ ioport_value const unscaled(((m_scrollyinput_port->read() & m_scrollymask) >> m_scrollyshift) - m_scrollymin);
+ float const range(std::make_signed_t<ioport_value>(m_scrollymax - m_scrollymin) + (!Wrap ? 0 : (m_scrollymin < m_scrollymax) ? 1 : -1));
+ return float(std::make_signed_t<ioport_value>(unscaled)) / range;
+}
+
+
+//-------------------------------------------------
+// get_interpolated_bounds - animated bounds
+//-------------------------------------------------
+
+render_bounds layout_view_item::get_interpolated_bounds() const
+{
+ assert(m_bounds.size() > 1U);
+ return interpolate_bounds(m_bounds, m_get_anim_state());
+}
+
+
+//-------------------------------------------------
+// get_interpolated_color - animated color
+//-------------------------------------------------
+
+render_color layout_view_item::get_interpolated_color() const
+{
+ assert(m_color.size() > 1U);
+ return interpolate_color(m_color, m_get_anim_state());
+}
+
+
+//-------------------------------------------------
// find_element - find element definition
-//---------------------------------------------
+//-------------------------------------------------
-layout_element *layout_view::item::find_element(environment &env, util::xml::data_node const &itemnode, element_map &elemmap)
+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));
- if (!name)
+ std::string const name(env.get_attribute_string(itemnode, !strcmp(itemnode.get_name(), "element") ? "ref" : "element"));
+ if (name.empty())
return nullptr;
// search the list of elements for a match, error if not found
@@ -3446,66 +5244,87 @@ layout_element *layout_view::item::find_element(environment &env, util::xml::dat
}
-//---------------------------------------------
+//-------------------------------------------------
// make_bounds - get transformed bounds
-//---------------------------------------------
+//-------------------------------------------------
-render_bounds layout_view::item::make_bounds(
- environment &env,
+layout_view_item::bounds_vector layout_view_item::make_bounds(
+ view_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;
+ bounds_vector result;
+ for (util::xml::data_node const *bounds = itemnode.get_child("bounds"); bounds; bounds = bounds->get_next_sibling("bounds"))
+ {
+ if (!add_bounds_step(env, result, *bounds))
+ {
+ throw layout_syntax_error(
+ util::string_format(
+ "%s item has duplicate bounds for state",
+ itemnode.get_name()));
+ }
+ }
+ for (emu::render::detail::bounds_step &step : result)
+ {
+ render_bounds_transform(step.bounds, trans);
+ if (step.bounds.x0 > step.bounds.x1)
+ std::swap(step.bounds.x0, step.bounds.x1);
+ if (step.bounds.y0 > step.bounds.y1)
+ std::swap(step.bounds.y0, step.bounds.y1);
+ }
+ set_bounds_deltas(result);
+ return result;
}
-//---------------------------------------------
-// make_input_tag - get absolute input tag
-//---------------------------------------------
+//-------------------------------------------------
+// make_color - get color inflection points
+//-------------------------------------------------
-std::string layout_view::item::make_input_tag(environment &env, util::xml::data_node const &itemnode)
+layout_view_item::color_vector layout_view_item::make_color(
+ view_environment &env,
+ util::xml::data_node const &itemnode,
+ render_color const &mult)
{
- char const *tag(env.get_attribute_string(itemnode, "inputtag", nullptr));
- return tag ? env.device().subtag(tag) : std::string();
+ color_vector result;
+ for (util::xml::data_node const *color = itemnode.get_child("color"); color; color = color->get_next_sibling("color"))
+ {
+ if (!add_color_step(env, result, *color))
+ {
+ throw layout_syntax_error(
+ util::string_format(
+ "%s item has duplicate color for state",
+ itemnode.get_name()));
+ }
+ }
+ if (result.empty())
+ {
+ result.emplace_back(emu::render::detail::color_step{ 0, mult, { 0.0F, 0.0F, 0.0F, 0.0F } });
+ }
+ else
+ {
+ for (emu::render::detail::color_step &step : result)
+ step.color *= mult;
+ set_color_deltas(result);
+ }
+ return result;
}
-//---------------------------------------------
-// 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));
- }
+//**************************************************************************
+// LAYOUT VIEW VISIBILITY TOGGLE
+//**************************************************************************
- // fall back to implicit blend mode based on element type
- if (!strcmp(itemnode.get_name(), "screen"))
- return -1; // magic number recognised by render.cpp to allow per-element blend mode
- else if (!strcmp(itemnode.get_name(), "overlay"))
- return BLENDMODE_RGB_MULTIPLY;
- else
- return BLENDMODE_ALPHA;
+//-------------------------------------------------
+// visibility_toggle - constructor
+//-------------------------------------------------
+
+layout_view::visibility_toggle::visibility_toggle(std::string &&name, u32 mask)
+ : m_name(std::move(name))
+ , m_mask(mask)
+{
+ assert(mask);
}
@@ -3518,13 +5337,18 @@ int layout_view::item::get_blend_mode(environment &env, util::xml::data_node con
// layout_file - constructor
//-------------------------------------------------
-layout_file::layout_file(device_t &device, util::xml::data_node const &rootnode, char const *dirname)
- : m_elemmap()
+layout_file::layout_file(
+ device_t &device,
+ util::xml::data_node const &rootnode,
+ char const *searchpath,
+ char const *dirname)
+ : m_device(device)
+ , m_elemmap()
, m_viewlist()
{
try
{
- environment env(device);
+ environment env(device, searchpath, dirname);
// find the layout node
util::xml::data_node const *const mamelayoutnode = rootnode.get_child("mamelayout");
@@ -3538,13 +5362,13 @@ layout_file::layout_file(device_t &device, util::xml::data_node const &rootnode,
// parse all the parameters, elements and groups
group_map groupmap;
- add_elements(dirname, env, *mamelayoutnode, groupmap, false, true);
+ add_elements(env, *mamelayoutnode, groupmap, false, true);
// parse all the views
for (util::xml::data_node const *viewnode = mamelayoutnode->get_child("view"); viewnode != nullptr; viewnode = viewnode->get_next_sibling("view"))
{
// the trouble with allowing errors to propagate here is that it wreaks havoc with screenless systems that use a terminal by default
- // e.g. intlc44 and intlc440 have a terminal on the tty port by default and have a view with the front panel with the terminal screen
+ // e.g. intlc44 and intlc440 have a terminal on the TTY port by default and have a view with the front panel with the terminal screen
// however, they have a second view with just the front panel which is very useful if you're using e.g. -tty null_modem with a socket
// if the error is allowed to propagate, the entire layout is dropped so you can't select the useful view
try
@@ -3553,9 +5377,17 @@ layout_file::layout_file(device_t &device, util::xml::data_node const &rootnode,
}
catch (layout_reference_error const &err)
{
- osd_printf_warning("Error instantiating layout view %s: %s\n", env.get_attribute_string(*viewnode, "name", ""), err.what());
+ osd_printf_warning("Error instantiating layout view %s: %s\n", env.get_attribute_string(*viewnode, "name"), err.what());
}
}
+
+ // load the content of the first script node
+ if (!m_viewlist.empty())
+ {
+ util::xml::data_node const *const scriptnode = mamelayoutnode->get_child("script");
+ if (scriptnode)
+ emulator_info::layout_script_cb(*this, scriptnode->get_value());
+ }
}
catch (layout_syntax_error const &err)
{
@@ -3574,8 +5406,32 @@ layout_file::~layout_file()
}
+//-------------------------------------------------
+// resolve_tags - resolve tags
+//-------------------------------------------------
+
+void layout_file::resolve_tags()
+{
+ for (layout_view &view : views())
+ view.resolve_tags();
+
+ if (!m_resolve_tags.isnull())
+ m_resolve_tags();
+}
+
+
+//-------------------------------------------------
+// set_resolve_tags_callback - set callback for
+// additional tasks after resolving tags
+//-------------------------------------------------
+
+void layout_file::set_resolve_tags_callback(resolve_tags_delegate &&handler)
+{
+ m_resolve_tags = std::move(handler);
+}
+
+
void layout_file::add_elements(
- char const *dirname,
environment &env,
util::xml::data_node const &parentnode,
group_map &groupmap,
@@ -3593,17 +5449,17 @@ void layout_file::add_elements(
}
else if (!strcmp(childnode->get_name(), "element"))
{
- char const *const name(env.get_attribute_string(*childnode, "name", nullptr));
- if (!name)
- throw layout_syntax_error("element lacks name attribute");
- if (!m_elemmap.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(env, *childnode, dirname)).second)
+ std::string_view const name(env.get_attribute_string(*childnode, "name"));
+ if (name.empty())
+ throw layout_syntax_error("element must have non-empty name attribute");
+ if (!m_elemmap.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(env, *childnode)).second)
throw layout_syntax_error(util::string_format("duplicate element name %s", name));
}
else if (!strcmp(childnode->get_name(), "group"))
{
- char const *const name(env.get_attribute_string(*childnode, "name", nullptr));
- if (!name)
- throw layout_syntax_error("group lacks name attribute");
+ std::string_view const name(env.get_attribute_string(*childnode, "name"));
+ if (name.empty())
+ throw layout_syntax_error("group must have non-empty name attribute");
if (!groupmap.emplace(std::piecewise_construct, std::forward_as_tuple(name), std::forward_as_tuple(*childnode)).second)
throw layout_syntax_error(util::string_format("duplicate group name %s", name));
}
@@ -3615,7 +5471,7 @@ void layout_file::add_elements(
environment local(env);
for (int i = 0; count > i; ++i)
{
- add_elements(dirname, local, *childnode, groupmap, true, !i);
+ add_elements(local, *childnode, groupmap, true, !i);
local.increment_parameters();
}
}