diff options
Diffstat (limited to 'src/emu/render.cpp')
-rw-r--r-- | src/emu/render.cpp | 320 |
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; }); } } |