summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorOleg Oshmyan <chortos@inbox.lv>2022-11-11 16:11:27 +0200
committerOleg Oshmyan <chortos@inbox.lv>2023-02-09 16:32:45 +0200
commit077328ca6715e2e2826881003946640f56cb763c (patch)
tree1c5fb7e83167ef3544a373a7efd557086b1a8457
parent545bc09b650f6bad9b606e0fecd949bcabc3fa94 (diff)
downloadlibass-077328ca6715e2e2826881003946640f56cb763c.tar.bz2
libass-077328ca6715e2e2826881003946640f56cb763c.tar.xz
Minimize rounding error in clip rectangle coordinates
For \clip and Scroll effects, scaled coordinates may not be integers. Round each coordinate to the nearest integer, not towards zero. Use lround as opposed to lrint to ensure smoother animations in case of e. g. a 1px animation step at a 1.5x scaling factor: lround gives successive steps of 2px, 1px, 2, 1, 2, 1, 2, 1, 2... whereas lrint would give 2, 1, 1, 2, 2, 1, 1, 2, 2... (The same reasoning has previously been applied to \kf coordinates in render_and_combine_glyphs.) For what it's worth, for Scroll effects, this exactly matches all VSFilter variants and setups except specific rare cases on MPC-HC's and MPC-BE's internal subtitle renderers where PlayRes differs from display resolution (or video storage resolution if the renderer is built as a standalone DirectShow filter) and MPC ends up off by 1px after double-rounding scaled Scroll boundaries. However, for rectangular \clip, all VSFilters truncate the scaled coordinates, and XySubFilter truncates them twice: at video-storage *and* display resolution. Furthermore, they compute \clip\t(\clip) relative to the truncated scaled starting coordinates, whereas libass currently computes it relative to the truncated PlayRes starting coordinates. Events that have \clip are additionally clipped to the video area, but the old code used floating-point arithmetic and rounded the result also towards zero. For practical values of PlayRes and display size on practical machines with a 53-bit double-precision float mantissa, this arithmetic is exact and no loss of precision occurs, although with a theoretical huge resolution this could lead to the rightmost pixel column or bottommost pixel row of the video area to be falsely clipped off (if PlayRes * display res / PlayRes < display res). But at any rate, this arithmetic isn't necessary at all: just use the known corresponding integer values.
-rw-r--r--libass/ass_render.c20
1 files changed, 10 insertions, 10 deletions
diff --git a/libass/ass_render.c b/libass/ass_render.c
index 7147f73..73e49f6 100644
--- a/libass/ass_render.c
+++ b/libass/ass_render.c
@@ -2980,20 +2980,20 @@ ass_render_event(RenderContext *state, ASS_Event *event,
// fix clip coordinates
if (state->explicit || !render_priv->settings.use_margins) {
state->clip_x0 =
- x2scr_pos_scaled(render_priv, state->clip_x0);
+ lround(x2scr_pos_scaled(render_priv, state->clip_x0));
state->clip_x1 =
- x2scr_pos_scaled(render_priv, state->clip_x1);
+ lround(x2scr_pos_scaled(render_priv, state->clip_x1));
state->clip_y0 =
- y2scr_pos(render_priv, state->clip_y0);
+ lround(y2scr_pos(render_priv, state->clip_y0));
state->clip_y1 =
- y2scr_pos(render_priv, state->clip_y1);
+ lround(y2scr_pos(render_priv, state->clip_y1));
if (state->explicit) {
// we still need to clip against screen boundaries
- double zx = x2scr_pos_scaled(render_priv, 0);
- double zy = y2scr_pos(render_priv, 0);
- double sx = x2scr_pos_scaled(render_priv, render_priv->track->PlayResX);
- double sy = y2scr_pos(render_priv, render_priv->track->PlayResY);
+ int zx = render_priv->settings.left_margin;
+ int zy = render_priv->settings.top_margin;
+ int sx = zx + render_priv->frame_content_width;
+ int sy = zy + render_priv->frame_content_height;
state->clip_x0 = FFMAX(state->clip_x0, zx);
state->clip_y0 = FFMAX(state->clip_y0, zy);
@@ -3009,8 +3009,8 @@ ass_render_event(RenderContext *state, ASS_Event *event,
}
if (state->evt_type & EVENT_VSCROLL) {
- double y0 = y2scr_pos(render_priv, state->scroll_y0);
- double y1 = y2scr_pos(render_priv, state->scroll_y1);
+ int y0 = lround(y2scr_pos(render_priv, state->scroll_y0));
+ int y1 = lround(y2scr_pos(render_priv, state->scroll_y1));
state->clip_y0 = FFMAX(state->clip_y0, y0);
state->clip_y1 = FFMIN(state->clip_y1, y1);