summaryrefslogtreecommitdiffstatshomepage
path: root/src/emu
diff options
context:
space:
mode:
author antonioginer <antonioginer@users.noreply.github.com>2022-01-05 18:26:54 +0000
committer GitHub <noreply@github.com>2022-01-06 05:26:54 +1100
commit7a6749ab8632eb6a3b01237c4af620aa366e5117 (patch)
tree56d207026439bfb2d2a729ae18dceeb46e2436dc /src/emu
parent51e318e1001760d9c7ae2d15749c94da51d31064 (diff)
emu/render.cpp: Improved scale factor selection. (#8961)
Fixes aspect related issues, undesired overscan, etc. (GitHub #8209, GitHub #8387, MT08110)
Diffstat (limited to 'src/emu')
-rw-r--r--src/emu/render.cpp122
1 files changed, 81 insertions, 41 deletions
diff --git a/src/emu/render.cpp b/src/emu/render.cpp
index d1fe139c08c..2c4f0d8ffbf 100644
--- a/src/emu/render.cpp
+++ b/src/emu/render.cpp
@@ -1197,63 +1197,103 @@ void render_target::compute_visible_area(s32 target_width, s32 target_height, fl
// apply orientation if required
if (target_orientation & ORIENTATION_SWAP_XY)
- src_aspect = 1.0 / src_aspect;
+ src_aspect = 1.0f / src_aspect;
- // get target aspect
- float target_aspect = (float)target_width / (float)target_height * target_pixel_aspect;
+ // we need the ratio of target to source aspect
+ float aspect_ratio = m_keepaspect ? (float)target_width / (float)target_height * target_pixel_aspect / src_aspect : 1.0f;
+
+ // first compute (a, b) scale factors to fit the screen
+ float a = (float)target_width / src_width;
+ float b = (float)target_height / src_height;
// apply automatic axial stretching if required
int scale_mode = m_scale_mode;
- if (m_scale_mode == SCALE_FRACTIONAL_AUTO)
- {
- bool is_rotated = (m_manager.machine().system().flags & ORIENTATION_SWAP_XY) ^ (target_orientation & ORIENTATION_SWAP_XY);
- scale_mode = is_rotated ? SCALE_FRACTIONAL_Y : SCALE_FRACTIONAL_X;
- }
+ if (scale_mode == SCALE_FRACTIONAL_AUTO)
+ scale_mode = (m_manager.machine().system().flags & ORIENTATION_SWAP_XY) ^ (target_orientation & ORIENTATION_SWAP_XY) ?
+ SCALE_FRACTIONAL_Y : SCALE_FRACTIONAL_X;
- // first compute scale factors to fit the screen
- float xscale = (float)target_width / src_width;
- float yscale = (float)target_height / src_height;
+ // determine the scaling method for each axis
+ bool a_is_fract = (scale_mode == SCALE_FRACTIONAL_X || scale_mode == SCALE_FRACTIONAL);
+ bool b_is_fract = (scale_mode == SCALE_FRACTIONAL_Y || scale_mode == SCALE_FRACTIONAL);
- // apply aspect correction
- if (m_keepaspect)
- {
- if (target_aspect > src_aspect)
- xscale *= src_aspect / target_aspect;
- else
- yscale *= target_aspect / src_aspect;
- }
+ // check if we have user defined scale factors, if so use them instead, but only on integer axes
+ int a_user = a_is_fract ? 0 : m_int_scale_x;
+ int b_user = b_is_fract ? 0 : m_int_scale_y;
- bool x_fits = render_round_nearest(xscale) * src_width <= target_width;
- bool y_fits = render_round_nearest(yscale) * src_height <= target_height;
+ // we allow overscan either explicitely or if integer scale factors are forced by user
+ bool int_overscan = m_int_overscan || (m_keepaspect && (a_user != 0 || b_user != 0));
+ float a_max = std::max(a, (float)a_user);
+ float b_max = std::max(b, (float)b_user);
- // compute integer scale factors
- float integer_x = std::max(1.0f, float(m_int_overscan || x_fits ? render_round_nearest(xscale) : floor(xscale)));
- float integer_y = std::max(1.0f, float(m_int_overscan || y_fits ? render_round_nearest(yscale) : floor(yscale)));
- // check if we have user defined scale factors, if so use them instead
- integer_x = m_int_scale_x > 0 ? m_int_scale_x : integer_x;
- integer_y = m_int_scale_y > 0 ? m_int_scale_y : integer_y;
+ // get the usable bounding box considering the type of scaling for each axis
+ float usable_aspect = (a_is_fract ? a : std::max(1.0f, floorf(a))) * src_width /
+ ((b_is_fract ? b : std::max(1.0f, floorf(b))) * src_height) * target_pixel_aspect;
- // now apply desired scale mode
- if (scale_mode == SCALE_FRACTIONAL_X)
+ // depending on the relative shape between target and source, let's define 'a' and 'b' so that:
+ // * a is the leader axis (first to hit a boundary)
+ // * b is the follower axis
+ if (usable_aspect > src_aspect)
{
- if (m_keepaspect) xscale *= integer_y / yscale;
- yscale = integer_y;
+ std::swap(a, b);
+ std::swap(a_user, b_user);
+ std::swap(a_is_fract, b_is_fract);
+ std::swap(a_max, b_max);
+ aspect_ratio = 1.0f / aspect_ratio;
}
- else if (scale_mode == SCALE_FRACTIONAL_Y)
- {
- if (m_keepaspect) yscale *= integer_x / xscale;
- xscale = integer_x;
- }
- else
+
+ // now find an (a, b) pair that best fits our boundaries and scale options
+ float a_best = 1.0f, b_best = 1.0f;
+ float diff = 1000;
+
+ // fill (a0, a1) range
+ float u = a_user == 0 ? a : (float)a_user;
+ float a_range[] = {a_is_fract ? u : std::max(1.0f, floorf(u)), a_is_fract ? u : std::max(1.0f, roundf(u))};
+
+ for (float aa : a_range)
{
- xscale = integer_x;
- yscale = integer_y;
+ // apply aspect correction to 'b' axis if needed, considering resulting 'a' borders
+ float ba = b * (m_keepaspect ? aspect_ratio * (aa / a) : 1.0f);
+
+ // fill (b0, b1) range
+ float v = b_user == 0 ? ba : (float)b_user;
+ float b_range[] = {b_is_fract ? v : std::max(1.0f, floorf(v)), b_is_fract ? v : std::max(1.0f, roundf(v))};
+
+ for (float bb : b_range)
+ {
+ // we may need to propagate proportions back to 'a' axis
+ float ab = aa;
+ if (m_keepaspect && a_user == 0)
+ {
+ if (a_is_fract) ab *= (bb / ba);
+ else if (b_user != 0) ab = std::max(1.0f, roundf(ab * (bb / ba)));
+ }
+
+ // if overscan isn't allowed, discard values that exceed the usable bounding box, except a minimum of 1.0f
+ if (!int_overscan && ((ab > a_max && bb > 1.0f) || (bb > b_max && ab > 1.0f)))
+ continue;
+
+ // score the result
+ float new_diff = fabsf(aspect_ratio * (a / b) - (ab / bb));
+
+ if (new_diff <= diff)
+ {
+ diff = new_diff;
+ a_best = ab;
+ b_best = bb;
+ }
+ }
}
+ a = a_best;
+ b = b_best;
+
+ // restore orientation
+ if (usable_aspect > src_aspect)
+ std::swap(a, b);
// set the final width/height
- visible_width = render_round_nearest(src_width * xscale);
- visible_height = render_round_nearest(src_height * yscale);
+ visible_width = render_round_nearest(src_width * a);
+ visible_height = render_round_nearest(src_height * b);
break;
}
}