diff --git a/src/emu/emuopts.cpp b/src/emu/emuopts.cpp index 12d75930f40..35a8fae6333 100644 --- a/src/emu/emuopts.cpp +++ b/src/emu/emuopts.cpp @@ -91,8 +91,10 @@ const options_entry emu_options::s_option_entries[] = // render options { nullptr, nullptr, OPTION_HEADER, "CORE RENDER OPTIONS" }, { OPTION_KEEPASPECT ";ka", "1", OPTION_BOOLEAN, "constrain to the proper aspect ratio" }, - { OPTION_INTSCALEX ";sx", "0", OPTION_INTEGER, "set horizontal scale factor for integer scaled views."}, - { OPTION_INTSCALEY ";sy", "0", OPTION_INTEGER, "set vertical scale factor for integer scaled views."}, + { OPTION_UNEVENSTRETCH ";ues", "1", OPTION_BOOLEAN, "allow non-integer stretch factors" }, + { OPTION_UNEVENSTRETCHX ";uesx", "0", OPTION_BOOLEAN, "allow non-integer stretch factors only on horizontal axis"}, + { OPTION_INTSCALEX ";sx", "0", OPTION_INTEGER, "set horizontal integer scale factor."}, + { OPTION_INTSCALEY ";sy", "0", OPTION_INTEGER, "set vertical integer scale."}, // rotation options { nullptr, nullptr, OPTION_HEADER, "CORE ROTATION OPTIONS" }, diff --git a/src/emu/emuopts.h b/src/emu/emuopts.h index 3e80f79a065..a85c7485406 100644 --- a/src/emu/emuopts.h +++ b/src/emu/emuopts.h @@ -100,6 +100,8 @@ enum // core render options #define OPTION_KEEPASPECT "keepaspect" +#define OPTION_UNEVENSTRETCH "unevenstretch" +#define OPTION_UNEVENSTRETCHX "unevenstretchx" #define OPTION_INTSCALEX "intscalex" #define OPTION_INTSCALEY "intscaley" @@ -289,6 +291,8 @@ public: // core render options bool keep_aspect() const { return bool_value(OPTION_KEEPASPECT); } + bool uneven_stretch() const { return bool_value(OPTION_UNEVENSTRETCH); } + bool uneven_stretch_x() const { return bool_value(OPTION_UNEVENSTRETCHX); } int int_scale_x() const { return int_value(OPTION_INTSCALEX); } int int_scale_y() const { return int_value(OPTION_INTSCALEY); } diff --git a/src/emu/render.cpp b/src/emu/render.cpp index 9b4782c2b85..db4f1442ec2 100644 --- a/src/emu/render.cpp +++ b/src/emu/render.cpp @@ -933,8 +933,12 @@ render_target::render_target(render_manager &manager, const char *layoutfile, UI // aspect and scale options m_keepaspect = manager.machine().options().keep_aspect(); - m_int_scale_x = manager.machine().options().int_scale_x(); - m_int_scale_y = manager.machine().options().int_scale_y(); + m_int_scale_x = manager.machine().options().int_scale_x(); + m_int_scale_y = manager.machine().options().int_scale_y(); + if (manager.machine().options().uneven_stretch() && !manager.machine().options().uneven_stretch_x()) + m_scale_mode = SCALE_FRACTIONAL; + else + m_scale_mode = manager.machine().options().uneven_stretch_x()? SCALE_FRACTIONAL_X : SCALE_INTEGER; // determine the base orientation based on options if (!manager.machine().options().rotate()) @@ -1010,6 +1014,7 @@ void render_target::set_bounds(INT32 width, INT32 height, float pixel_aspect) m_bounds.x0 = m_bounds.y0 = 0; m_bounds.x1 = (float)width; m_bounds.y1 = (float)height; + m_pixel_aspect = pixel_aspect != 0.0? pixel_aspect : 1.0; } @@ -1144,41 +1149,90 @@ const render_screen_list &render_target::view_screens(int viewindex) void render_target::compute_visible_area(INT32 target_width, INT32 target_height, float target_pixel_aspect, int target_orientation, INT32 &visible_width, INT32 &visible_height) { - float width, height; - float scale; - - // constrained case - if (m_keepaspect) + switch (m_scale_mode) { - // start with the aspect ratio of the square pixel layout - width = m_curview->effective_aspect(m_layerconfig); - height = 1.0f; + case SCALE_FRACTIONAL: + { + float width, height; + float scale; - // first apply target orientation - if (target_orientation & ORIENTATION_SWAP_XY) - FSWAP(width, height); + // constrained case + if (m_keepaspect) + { + // start with the aspect ratio of the square pixel layout + width = m_curview->effective_aspect(m_layerconfig); + height = 1.0f; - // apply the target pixel aspect ratio - height *= target_pixel_aspect; + // first apply target orientation + if (target_orientation & ORIENTATION_SWAP_XY) + FSWAP(width, height); - // based on the height/width ratio of the source and target, compute the scale factor - if (width / height > (float)target_width / (float)target_height) - scale = (float)target_width / width; - else - scale = (float)target_height / height; + // apply the target pixel aspect ratio + height *= target_pixel_aspect; + + // based on the height/width ratio of the source and target, compute the scale factor + if (width / height > (float)target_width / (float)target_height) + scale = (float)target_width / width; + else + scale = (float)target_height / height; + } + + // stretch-to-fit case + else + { + width = (float)target_width; + height = (float)target_height; + scale = 1.0f; + } + + // set the final width/height + visible_width = render_round_nearest(width * scale); + visible_height = render_round_nearest(height * scale); + break; + } + + case SCALE_FRACTIONAL_X: + case SCALE_INTEGER: + { + INT32 src_width, src_height; + compute_minimum_size(src_width, src_height); + + float dest_width, dest_width_asp, dest_height, dest_height_asp; + dest_width = dest_width_asp = (float)target_width; + dest_height = dest_height_asp = (float)target_height; + + float src_aspect = m_curview->effective_aspect(m_layerconfig); + float dest_aspect = dest_width / dest_height * target_pixel_aspect; + + // We need to work out which one is the horizontal axis, regardless of the monitor orientation + float xscale, yscale; + if (dest_aspect > 1.0) + { + // x-axis matches monitor's horizontal dimension + dest_width_asp *= m_keepaspect? src_aspect / dest_aspect : 1.0; + xscale = m_scale_mode == SCALE_INTEGER? + MAX(1, render_round_nearest(dest_width_asp / src_width)) : dest_width_asp / src_width; + yscale = MAX(1, render_round_nearest(dest_height / src_height)); + } + else + { + // y-axis matches monitor's vertical dimension + dest_height_asp *= m_keepaspect? dest_aspect / src_aspect : 1.0; + yscale = m_scale_mode == SCALE_INTEGER? + MAX(1, render_round_nearest(dest_height_asp / src_height)) : dest_height_asp / src_height; + xscale = MAX(1, render_round_nearest(dest_width / src_width)); + } + + // Check if we have user defined scale factors, if so use them instead + xscale = m_int_scale_x? m_int_scale_x : xscale; + yscale = m_int_scale_y? m_int_scale_y : yscale; + + // set the final width/height + visible_width = render_round_nearest(src_width * xscale); + visible_height = render_round_nearest(src_height * yscale); + break; + } } - - // stretch-to-fit case - else - { - width = (float)target_width; - height = (float)target_height; - scale = 1.0f; - } - - // set the final width/height - visible_width = render_round_nearest(width * scale); - visible_height = render_round_nearest(height * scale); } diff --git a/src/emu/render.h b/src/emu/render.h index 37abd2abd5e..84fc3b95fdf 100644 --- a/src/emu/render.h +++ b/src/emu/render.h @@ -73,11 +73,13 @@ const UINT8 RENDER_CREATE_NO_ART = 0x01; // ignore any views that const UINT8 RENDER_CREATE_SINGLE_FILE = 0x02; // only load views from the file specified const UINT8 RENDER_CREATE_HIDDEN = 0x04; // don't make this target visible -// render scaling types -const UINT32 RENDER_SCALE_FRACTIONAL = 0x00; // compute bounds using dimensionless proportions (default) -const UINT32 RENDER_SCALE_INTEGER = 0x01; // compute integer scaling factors for both axes, based on target dimensions -const UINT32 RENDER_SCALE_STRETCH_H = 0x02; // compute fractional scaling factor for x-axis, and integer factor for y-axis -const UINT32 RENDER_SCALE_STRETCH_FULL = 0x03; // match bounds with physical target dimensions in pixels +// render scaling modes +enum +{ + SCALE_FRACTIONAL = 0, // compute fractional scaling factors for both axes + SCALE_FRACTIONAL_X, // compute fractional scaling factor for x-axis, and integer factor for y-axis + SCALE_INTEGER // compute integer scaling factors for both axes, based on target dimensions +}; // flags for primitives const int PRIMFLAG_TEXORIENT_SHIFT = 0; @@ -91,7 +93,6 @@ const UINT32 PRIMFLAG_BLENDMODE_MASK = 15 << PRIMFLAG_BLENDMODE_SHIFT; const int PRIMFLAG_ANTIALIAS_SHIFT = 12; const UINT32 PRIMFLAG_ANTIALIAS_MASK = 1 << PRIMFLAG_ANTIALIAS_SHIFT; - const int PRIMFLAG_SCREENTEX_SHIFT = 13; const UINT32 PRIMFLAG_SCREENTEX_MASK = 1 << PRIMFLAG_SCREENTEX_SHIFT; @@ -897,7 +898,7 @@ public: UINT32 width() const { return m_width; } UINT32 height() const { return m_height; } float pixel_aspect() const { return m_pixel_aspect; } - int scale_type() const { return m_scale_type; } + int scale_mode() const { return m_scale_mode; } float max_update_rate() const { return m_max_refresh; } int orientation() const { return m_orientation; } render_layer_config layer_config() const { return m_layerconfig; } @@ -1002,7 +1003,7 @@ private: render_bounds m_bounds; // bounds of the target bool m_keepaspect; // constrain aspect ratio float m_pixel_aspect; // aspect ratio of individual pixels - int m_scale_type; // type of scale to apply + int m_scale_mode; // type of scale to apply int m_int_scale_x; // horizontal integer scale factor int m_int_scale_y; // vertical integer scale factor float m_max_refresh; // maximum refresh rate, 0 or if none diff --git a/src/osd/sdl/window.cpp b/src/osd/sdl/window.cpp index 199e428afc8..338893670aa 100644 --- a/src/osd/sdl/window.cpp +++ b/src/osd/sdl/window.cpp @@ -1192,7 +1192,7 @@ osd_rect sdl_window_info::constrain_to_aspect_ratio(const osd_rect &rect, int ad osd_monitor_info *monitor = m_monitor; // do not constrain aspect ratio for integer scaled views - if (m_target->scale_type() != RENDER_SCALE_FRACTIONAL) + if (m_target->scale_mode() != SCALE_FRACTIONAL) return rect; // get the pixel aspect ratio for the target monitor @@ -1316,7 +1316,7 @@ osd_dim sdl_window_info::get_min_bounds(int constrain) minheight += wnd_extra_height(); // if we want it constrained, figure out which one is larger - if (constrain && m_target->scale_type() == RENDER_SCALE_FRACTIONAL) + if (constrain && m_target->scale_mode() == SCALE_FRACTIONAL) { // first constrain with no height limit osd_rect test1(0,0,minwidth,10000); @@ -1380,7 +1380,7 @@ osd_dim sdl_window_info::get_max_bounds(int constrain) maximum = maximum.resize(tempw, temph); // constrain to fit - if (constrain && m_target->scale_type() == RENDER_SCALE_FRACTIONAL) + if (constrain && m_target->scale_mode() == SCALE_FRACTIONAL) maximum = constrain_to_aspect_ratio(maximum, WMSZ_BOTTOMRIGHT); // remove extra window stuff diff --git a/src/osd/windows/window.cpp b/src/osd/windows/window.cpp index e0089d98585..28db75a7fe1 100644 --- a/src/osd/windows/window.cpp +++ b/src/osd/windows/window.cpp @@ -1663,7 +1663,7 @@ osd_rect win_window_info::constrain_to_aspect_ratio(const osd_rect &rect, int ad assert(GetCurrentThreadId() == window_threadid); // do not constrain aspect ratio for integer scaled views - if (m_target->scale_type() != RENDER_SCALE_FRACTIONAL) + if (m_target->scale_mode() != SCALE_FRACTIONAL) return rect; // get the pixel aspect ratio for the target monitor @@ -1787,7 +1787,7 @@ osd_dim win_window_info::get_min_bounds(int constrain) minheight += wnd_extra_height(); // if we want it constrained, figure out which one is larger - if (constrain && m_target->scale_type() == RENDER_SCALE_FRACTIONAL) + if (constrain && m_target->scale_mode() == SCALE_FRACTIONAL) { // first constrain with no height limit osd_rect test1(0,0,minwidth,10000); @@ -1847,7 +1847,7 @@ osd_dim win_window_info::get_max_bounds(int constrain) maximum = maximum.resize(tempw, temph); // constrain to fit - if (constrain && m_target->scale_type() == RENDER_SCALE_FRACTIONAL) + if (constrain && m_target->scale_mode() == SCALE_FRACTIONAL) maximum = constrain_to_aspect_ratio(maximum, WMSZ_BOTTOMRIGHT); return maximum.dim();