summaryrefslogtreecommitdiffstats
path: root/src/emu/render.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/emu/render.cpp')
-rw-r--r--src/emu/render.cpp320
1 files changed, 213 insertions, 107 deletions
diff --git a/src/emu/render.cpp b/src/emu/render.cpp
index 9ec8445fa49..a65a47e2cf6 100644
--- a/src/emu/render.cpp
+++ b/src/emu/render.cpp
@@ -46,8 +46,12 @@
#include "drivenum.h"
#include "xmlfile.h"
#include "ui/uimain.h"
+
#include <zlib.h>
+#include <algorithm>
+#include <functional>
+
//**************************************************************************
@@ -562,20 +566,20 @@ const rgb_t *render_texture::get_adjusted_palette(render_container &container)
//-------------------------------------------------
render_container::render_container(render_manager &manager, screen_device *screen)
- : m_next(nullptr),
- m_manager(manager),
- m_screen(screen),
- m_overlaybitmap(nullptr),
- m_overlaytexture(nullptr)
+ : m_next(nullptr)
+ , m_manager(manager)
+ , m_screen(screen)
+ , m_overlaybitmap(nullptr)
+ , m_overlaytexture(nullptr)
{
// make sure it is empty
empty();
// if we have a screen, read and apply the options
- if (m_screen != nullptr)
+ if (m_screen)
{
// set the initial orientation and brightness/contrast/gamma
- m_user.m_orientation = manager.machine().system().flags & machine_flags::MASK_ORIENTATION;
+ m_user.m_orientation = m_screen->orientation();
m_user.m_brightness = manager.machine().options().brightness();
m_user.m_contrast = manager.machine().options().contrast();
m_user.m_gamma = manager.machine().options().gamma();
@@ -884,14 +888,14 @@ void render_container::update_palette()
//-------------------------------------------------
render_container::user_settings::user_settings()
- : m_orientation(0),
- m_brightness(1.0f),
- m_contrast(1.0f),
- m_gamma(1.0f),
- m_xscale(1.0f),
- m_yscale(1.0f),
- m_xoffset(0.0f),
- m_yoffset(0.0f)
+ : m_orientation(0)
+ , m_brightness(1.0f)
+ , m_contrast(1.0f)
+ , m_gamma(1.0f)
+ , m_xscale(1.0f)
+ , m_yscale(1.0f)
+ , m_xoffset(0.0f)
+ , m_yoffset(0.0f)
{
}
@@ -1676,23 +1680,53 @@ void render_target::load_additional_layout_files(const char *basename, bool have
}
- screen_device_iterator iter(m_manager.machine().root_device());
- unsigned const screens = iter.count();
-
- // now do the built-in layouts for single-screen games
- if (screens == 1)
+ // local screen info to avoid repeated code
+ class screen_info
{
- if (system.flags & ORIENTATION_SWAP_XY)
- load_layout_file(nullptr, layout_vertical);
- else
- load_layout_file(nullptr, layout_horizont);
- if (m_filelist.empty())
- throw emu_fatalerror("Couldn't parse default layout??");
- }
+ public:
+ screen_info(screen_device const &screen)
+ : m_device(screen)
+ , m_rotated(screen.orientation() & ORIENTATION_SWAP_XY)
+ , m_physical(screen.physical_aspect())
+ , m_native(screen.visible_area().width(), screen.visible_area().height())
+ {
+ util::reduce_fraction(m_native.first, m_native.second);
+ if (m_rotated)
+ {
+ std::swap(m_physical.first, m_physical.second);
+ std::swap(m_native.first, m_native.second);
+ }
+ }
+
+ screen_device const &device() const { return m_device.get(); }
+ bool rotated() const { return m_rotated; }
+ bool square() const { return m_physical == m_native; }
+ unsigned physical_x() const { return m_physical.first; }
+ unsigned physical_y() const { return m_physical.second; }
+ unsigned native_x() const { return m_native.first; }
+ unsigned native_y() const { return m_native.second; }
+
+ std::pair<float, float> tiled_size() const
+ {
+ if (physical_x() == physical_y())
+ return std::make_pair(1.0F, 1.0F);
+ else if (physical_x() > physical_y())
+ return std::make_pair(1.0F, float(physical_y()) / physical_x());
+ else
+ return std::make_pair(float(physical_x()) / physical_y(), 1.0F);
+ }
+
+ private:
+ std::reference_wrapper<screen_device const> m_device;
+ bool m_rotated;
+ std::pair<unsigned, unsigned> m_physical, m_native;
+ };
+ screen_device_iterator iter(m_manager.machine().root_device());
+ std::vector<screen_info> const screens(std::begin(iter), std::end(iter));
if (!have_default && !have_artwork)
{
- if (screens == 2)
+ if (screens.size() == 2U)
{
load_layout_file(nullptr, layout_dualhsxs);
if (m_filelist.empty())
@@ -1700,7 +1734,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have
}
}
- if (screens == 0) // ensure the fallback view for systems with no screens is loaded if necessary
+ if (screens.empty()) // ensure the fallback view for systems with no screens is loaded if necessary
{
if (!view_by_index(0))
{
@@ -1709,7 +1743,7 @@ void render_target::load_additional_layout_files(const char *basename, bool have
throw emu_fatalerror("Couldn't parse default layout??");
}
}
- else if (screens >= 2) // generate default layouts for larger numbers of screens
+ else // generate default layouts for larger numbers of screens
{
util::xml::file::ptr const root(util::xml::file::create());
if (!root)
@@ -1719,17 +1753,18 @@ void render_target::load_additional_layout_files(const char *basename, bool have
throw emu_fatalerror("Couldn't create XML node??");
layoutnode->set_attribute_int("version", 2);
- // get standard width/height assuming 4:3 screens
- unsigned const stdwidth((system.flags & ORIENTATION_SWAP_XY) ? 3 : 4);
- unsigned const stdheight((system.flags & ORIENTATION_SWAP_XY) ? 4 : 3);
-
- // generate individual 4:3 views
- for (unsigned i = 0; screens > i; ++i)
+ // generate individual physical aspect views
+ for (unsigned i = 0; screens.size() > i; ++i)
{
util::xml::data_node *const viewnode(layoutnode->add_child("view", nullptr));
if (!viewnode)
throw emu_fatalerror("Couldn't create XML node??");
- viewnode->set_attribute("name", util::xml::normalize_string(util::string_format("Screen %1$u Standard (%2$u:%3$u)", i, stdwidth, stdheight).c_str()));
+ viewnode->set_attribute(
+ "name",
+ util::xml::normalize_string(
+ util::string_format(
+ "Screen %1$u Standard (%2$u:%3$u)",
+ i, screens[i].physical_x(), screens[i].physical_y()).c_str()));
util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr));
if (!screennode)
throw emu_fatalerror("Couldn't create XML node??");
@@ -1739,40 +1774,85 @@ void render_target::load_additional_layout_files(const char *basename, bool have
throw emu_fatalerror("Couldn't create XML node??");
boundsnode->set_attribute_int("x", 0);
boundsnode->set_attribute_int("y", 0);
- boundsnode->set_attribute_int("width", stdwidth);
- boundsnode->set_attribute_int("height", stdheight);
+ boundsnode->set_attribute_int("width", screens[i].physical_x());
+ boundsnode->set_attribute_int("height", screens[i].physical_y());
}
// generate individual pixel aspect views
- for (unsigned i = 0; screens > i; ++i)
+ for (unsigned i = 0; screens.size() > i; ++i)
+ {
+ if (!screens[i].square())
+ {
+ util::xml::data_node *const viewnode(layoutnode->add_child("view", nullptr));
+ if (!viewnode)
+ throw emu_fatalerror("Couldn't create XML node??");
+ viewnode->set_attribute(
+ "name",
+ util::xml::normalize_string(
+ util::string_format(
+ "Screen %1$u Pixel Aspect (%2$u:%3$u)",
+ i, screens[i].native_x(), screens[i].native_y()).c_str()));
+ util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr));
+ if (!screennode)
+ throw emu_fatalerror("Couldn't create XML node??");
+ screennode->set_attribute_int("index", i);
+ util::xml::data_node *const boundsnode(screennode->add_child("bounds", nullptr));
+ if (!boundsnode)
+ throw emu_fatalerror("Couldn't create XML node??");
+ boundsnode->set_attribute_int("x", 0);
+ boundsnode->set_attribute_int("y", 0);
+ boundsnode->set_attribute_int("width", screens[i].native_x());
+ boundsnode->set_attribute_int("height", screens[i].native_y());
+ }
+ }
+
+ // generate the fake cocktail view for single-screen systems
+ if (screens.size() == 1U)
{
util::xml::data_node *const viewnode(layoutnode->add_child("view", nullptr));
if (!viewnode)
throw emu_fatalerror("Couldn't create XML node??");
- viewnode->set_attribute("name", util::xml::normalize_string(util::string_format("Screen %1$u Pixel Aspect (~scr%1$unativexaspect~:~scr%1$unativeyaspect~)", i).c_str()));
+ viewnode->set_attribute("name", "Cocktail");
+
+ util::xml::data_node *const mirrornode(viewnode->add_child("screen", nullptr));
+ if (!mirrornode)
+ throw emu_fatalerror("Couldn't create XML node??");
+ mirrornode->set_attribute_int("index", 0);
+ util::xml::data_node *const mirrorbounds(mirrornode->add_child("bounds", nullptr));
+ if (!mirrorbounds)
+ throw emu_fatalerror("Couldn't create XML node??");
+ mirrorbounds->set_attribute_int("x", 0);
+ mirrorbounds->set_attribute_float("y", (-0.01 * (std::min)(screens[0].physical_x(), screens[0].physical_y())) - screens[0].physical_y());
+ mirrorbounds->set_attribute_int("width", screens[0].physical_x());
+ mirrorbounds->set_attribute_int("height", screens[0].physical_y());
+ util::xml::data_node *const flipper(mirrornode->add_child("orientation", nullptr));
+ if (!flipper)
+ throw emu_fatalerror("Couldn't create XML node??");
+ flipper->set_attribute_int("rotate", 180);
+
util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr));
if (!screennode)
throw emu_fatalerror("Couldn't create XML node??");
- screennode->set_attribute_int("index", i);
- util::xml::data_node *const boundsnode(screennode->add_child("bounds", nullptr));
- if (!boundsnode)
+ screennode->set_attribute_int("index", 0);
+ util::xml::data_node *const screenbounds(screennode->add_child("bounds", nullptr));
+ if (!screenbounds)
throw emu_fatalerror("Couldn't create XML node??");
- boundsnode->set_attribute_int("x", 0);
- boundsnode->set_attribute_int("y", 0);
- boundsnode->set_attribute("width", util::xml::normalize_string(util::string_format("~scr%1$uwidth~", i).c_str()));
- boundsnode->set_attribute("height", util::xml::normalize_string(util::string_format("~scr%1$uheight~", i).c_str()));
+ screenbounds->set_attribute_int("x", 0);
+ screenbounds->set_attribute_int("y", 0);
+ screenbounds->set_attribute_int("width", screens[0].physical_x());
+ screenbounds->set_attribute_int("height", screens[0].physical_y());
}
// generate tiled views if the supplied artwork doesn't provide a view of all screens
- bool need_tiles(screens >= 3);
- if (!need_tiles)
+ bool need_tiles(screens.size() >= 3);
+ if (!need_tiles && (screens.size() >= 2))
{
need_tiles = true;
int viewindex(0);
for (layout_view *view = view_by_index(viewindex); need_tiles && view; view = view_by_index(++viewindex))
{
render_screen_list const &viewscreens(view->screens());
- if (viewscreens.count() >= screens)
+ if (viewscreens.count() >= screens.size())
{
bool screen_missing(false);
for (screen_device &screen : iter)
@@ -1790,93 +1870,119 @@ void render_target::load_additional_layout_files(const char *basename, bool have
}
if (need_tiles)
{
- // helper for generating a view since we do this a lot
+ // helpers for generating a view since we do this a lot
+ std::vector<float> widths(screens.size()), heights(screens.size());
+ std::vector<std::pair<float, float> > sizes(screens.size());
+ std::transform(screens.begin(), screens.end(), sizes.begin(), [] (screen_info const &s) { return s.tiled_size(); });
auto const generate_view =
- [&layoutnode, screens, stdwidth, stdheight] (char const *title, auto &&bounds_callback)
+ [&layoutnode, &screens, &widths, &heights, &sizes] (char const *title, unsigned columns, bool gapless, auto &&mapper)
{
+ // calculate necessary widths/heights of rows/columns restricting screens to unit square
+ assert(0U < columns);
+ assert(screens.size() >= columns);
+ unsigned const rows((screens.size() + columns - 1) / columns);
+ std::fill_n(widths.begin(), columns, 0.0F);
+ std::fill_n(heights.begin(), rows, 0.0F);
+ for (unsigned y = 0U; rows > y; ++y)
+ {
+ for (unsigned x = 0U; columns > x; ++x)
+ {
+ int const i(mapper(x, y));
+ if (0 <= i)
+ {
+ widths[x] = (std::max)(widths[x], sizes[i].first);
+ heights[y] = (std::max)(heights[y], sizes[i].second);
+ }
+ }
+ }
+
+ // spacing is 1% of minor dimension
+ float spacing(0.0F);
+ if (!gapless)
+ {
+ spacing = 0.01F * (std::min)(
+ *std::max_element(widths.begin(), widths.begin() + columns),
+ *std::max_element(heights.begin(), heights.begin() + rows));
+ }
+
+ // actually generate elements
util::xml::data_node *viewnode = layoutnode->add_child("view", nullptr);
if (!viewnode)
throw emu_fatalerror("Couldn't create XML node??");
viewnode->set_attribute("name", util::xml::normalize_string(title));
- for (unsigned i = 0; screens > i; ++i)
+ float ypos(0.0F);
+ for (unsigned y = 0U; rows > y; ypos += heights[y] + spacing, ++y)
{
- util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr));
- if (!screennode)
- throw emu_fatalerror("Couldn't create XML node??");
- screennode->set_attribute_int("index", i);
- util::xml::data_node *const boundsnode(screennode->add_child("bounds", nullptr));
- if (!boundsnode)
- throw emu_fatalerror("Couldn't create XML node??");
- bounds_callback(*boundsnode, i);
- boundsnode->set_attribute_int("width", stdwidth);
- boundsnode->set_attribute_int("height", stdheight);
+ float xpos(0.0F);
+ for (unsigned x = 0U; columns > x; xpos += widths[x] + spacing, ++x)
+ {
+ int const i(mapper(x, y));
+ if (0 <= i)
+ {
+ util::xml::data_node *const screennode(viewnode->add_child("screen", nullptr));
+ if (!screennode)
+ throw emu_fatalerror("Couldn't create XML node??");
+ screennode->set_attribute_int("index", i);
+ util::xml::data_node *const boundsnode(screennode->add_child("bounds", nullptr));
+ if (!boundsnode)
+ throw emu_fatalerror("Couldn't create XML node??");
+ boundsnode->set_attribute_float("x", xpos + ((widths[x] - sizes[i].first) / 2));
+ boundsnode->set_attribute_float("y", ypos + ((heights[y] - sizes[i].second) / 2));
+ boundsnode->set_attribute_float("width", sizes[i].first);
+ boundsnode->set_attribute_float("height", sizes[i].second);
+ }
+ }
}
};
// generate linear views
- generate_view(
- "Left-to-Right",
- [stdwidth] (util::xml::data_node &boundsnode, unsigned i)
- {
- boundsnode.set_attribute_float("x", i * (stdwidth + 0.03f));
- boundsnode.set_attribute_int("y", 0);
- });
- generate_view(
- "Left-to-Right (Gapless)",
- [stdwidth] (util::xml::data_node &boundsnode, unsigned i)
- {
- boundsnode.set_attribute_int("x", i * stdwidth);
- boundsnode.set_attribute_int("y", 0);
- });
- generate_view(
- "Top-to-Bottom",
- [stdheight] (util::xml::data_node &boundsnode, unsigned i)
- {
- boundsnode.set_attribute_int("x", 0);
- boundsnode.set_attribute_float("y", i * (stdheight + 0.03f));
- });
- generate_view(
- "Top-to-Bottom (Gapless)",
- [stdheight] (util::xml::data_node &boundsnode, unsigned i)
- {
- boundsnode.set_attribute_int("x", 0);
- boundsnode.set_attribute_int("y", i * stdheight);
- });
+ generate_view("Left-to-Right", screens.size(), false, [] (unsigned x, unsigned y) { return x; });
+ generate_view("Left-to-Right (Gapless)", screens.size(), true, [] (unsigned x, unsigned y) { return x; });
+ generate_view("Top-to-Bottom", 1U, false, [] (unsigned x, unsigned y) { return y; });
+ generate_view("Top-to-Bottom (Gapless)", 1U, true, [] (unsigned x, unsigned y) { return y; });
// generate tiled views
- for (unsigned mindim = 2; ((screens + mindim - 1) / mindim) >= mindim; ++mindim)
+ for (unsigned mindim = 2; ((screens.size() + mindim - 1) / mindim) >= mindim; ++mindim)
{
- unsigned const majdim((screens + mindim - 1) / mindim);
- unsigned const remainder(screens % majdim);
+ unsigned const majdim((screens.size() + mindim - 1) / mindim);
+ unsigned const remainder(screens.size() % majdim);
if (!remainder || (((majdim + 1) / 2) <= remainder))
{
generate_view(
util::string_format("%1$u\xC3\x97%2$u Left-to-Right, Top-to-Bottom", majdim, mindim).c_str(),
- [majdim, stdwidth, stdheight] (util::xml::data_node &boundsnode, unsigned i)
+ majdim,
+ false,
+ [&screens, majdim] (unsigned x, unsigned y)
{
- boundsnode.set_attribute_float("x", (i % majdim) * (stdwidth + 0.03f));
- boundsnode.set_attribute_float("y", (i / majdim) * (stdheight + 0.03f));
+ unsigned const i(x + (y * majdim));
+ return (screens.size() > i) ? int(i) : -1;
});
generate_view(
util::string_format("%1$u\xC3\x97%2$u Left-to-Right, Top-to-Bottom (Gapless)", majdim, mindim).c_str(),
- [majdim, stdwidth, stdheight] (util::xml::data_node &boundsnode, unsigned i)
+ majdim,
+ true,
+ [&screens, majdim] (unsigned x, unsigned y)
{
- boundsnode.set_attribute_int("x", (i % majdim) * stdwidth);
- boundsnode.set_attribute_int("y", (i / majdim) * stdheight);
+ unsigned const i(x + (y * majdim));
+ return (screens.size() > i) ? int(i) : -1;
});
generate_view(
util::string_format("%1$u\xC3\x97%2$u Top-to-Bottom, Left-to-Right", mindim, majdim).c_str(),
- [majdim, stdwidth, stdheight] (util::xml::data_node &boundsnode, unsigned i)
+ mindim,
+ false,
+ [&screens, majdim] (unsigned x, unsigned y)
{
- boundsnode.set_attribute_float("x", (i / majdim) * (stdwidth + 0.03f));
- boundsnode.set_attribute_float("y", (i % majdim) * (stdheight + 0.03f));
+ unsigned const i((x * majdim) + y);
+ return (screens.size() > i) ? int(i) : -1;
});
generate_view(
util::string_format("%1$u\xC3\x97%2$u Top-to-Bottom, Left-to-Right (Gapless)", mindim, majdim).c_str(),
- [majdim, stdwidth, stdheight] (util::xml::data_node &boundsnode, unsigned i)
+ mindim,
+ true,
+ [&screens, majdim] (unsigned x, unsigned y)
{
- boundsnode.set_attribute_int("x", (i / majdim) * stdwidth);
- boundsnode.set_attribute_int("y", (i % majdim) * stdheight);
+ unsigned const i((x * majdim) + y);
+ return (screens.size() > i) ? int(i) : -1;
});
}
}