diff options
author | Oleg Oshmyan <chortos@inbox.lv> | 2022-11-11 16:11:27 +0200 |
---|---|---|
committer | Oleg Oshmyan <chortos@inbox.lv> | 2023-02-09 16:32:45 +0200 |
commit | 077328ca6715e2e2826881003946640f56cb763c (patch) | |
tree | 1c5fb7e83167ef3544a373a7efd557086b1a8457 | |
parent | 545bc09b650f6bad9b606e0fecd949bcabc3fa94 (diff) | |
download | libass-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.c | 20 |
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); |