Skip to content

Commit

Permalink
Implement split vertical margin properties MarginB/T
Browse files Browse the repository at this point in the history
MarginB Style and Event are ignored when not in v4++ mode,
but as style overrides all Margin* fields act independent of version
with MarginV always setting both fields and MarinT/B only ever one.

In order to not break API, some complexity had to be introduced:

To keep API compatibility with styles created via API, we need to decide
at rendering time between using the styles MarginV or MarginB as the
bottom margin based on track version. Since such styles never go through
our parsing logic and MarginB didn't exist until now, they'll might have
MarginB set to 0 but expect MarginV to apply to both.

To keep API-compatibility with a mix of API-created styles and user
overrides, it is necessary to track per style if it was affected by
a MarginT or MarginB override. If either was overriden, we'll use
the split margin values at render time even for non-v4++ files.
Access to MarginB needs to be tracked separate from MarginT, otherwise
incompatibilities can still arise. Consider an override applied
to an API-created style which originally didn't set MarginB:
If we didn't know whether MarginB was already set by an override,
overrinding only MarginT would either suddenly set MarginB to zero
or destroy the value set by a previous override.

After a potential future API break, renaming the MarginV struct member
to MarginT and requiring API-created styles to set both independent of
version, all this complexitiy can be dropped.

TODO: Before merging the override bitmap logic should best be checked
carefully again as it wasn't examined or tested carefully yet.
  • Loading branch information
TheOneric committed Oct 4, 2022
1 parent 9f318e4 commit a1e3795
Show file tree
Hide file tree
Showing 5 changed files with 63 additions and 13 deletions.
53 changes: 46 additions & 7 deletions libass/ass.c
Original file line number Diff line number Diff line change
Expand Up @@ -81,12 +81,6 @@ void ass_free_track(ASS_Track *track)
if (!track)
return;

if (track->parser_priv) {
free(track->parser_priv->read_order_bitmap);
free(track->parser_priv->fontname);
free(track->parser_priv->fontdata);
free(track->parser_priv);
}
free(track->style_format);
free(track->event_format);
free(track->Language);
Expand All @@ -101,6 +95,14 @@ void ass_free_track(ASS_Track *track)
}
free(track->events);
free(track->name);
if (track->parser_priv) {
free(track->parser_priv->read_order_bitmap);
free(track->parser_priv->fontname);
free(track->parser_priv->fontdata);
free(track->parser_priv->override_marginbt);
free(track->parser_priv->override_marginb);
free(track->parser_priv);
}
free(track);
}

Expand All @@ -119,6 +121,17 @@ int ass_alloc_style(ASS_Track *track)
int new_max = track->max_styles + ASS_STYLES_ALLOC;
if (!ASS_REALLOC_ARRAY(track->styles, new_max))
return -1;

// TODO: check especially this block again before merge
ASS_ParserPriv *priv = track->parser_priv;
size_t new_size = FFMAX(new_max + (size_t) 31, (size_t) new_max) / 32;
if (!ASS_REALLOC_ARRAY(priv->override_marginbt, new_size) ||
!ASS_REALLOC_ARRAY(priv->override_marginb, new_size))
return -1;
size_t old_size = FFMAX(track->max_styles + (size_t) 31, (size_t) track->max_styles) / 32;
memset(priv->override_marginbt + old_size, 0, new_size - old_size);
memset(priv->override_marginb + old_size, 0, new_size - old_size);

track->max_styles = new_max;
}

Expand Down Expand Up @@ -167,6 +180,9 @@ void ass_free_style(ASS_Track *track, int sid)

free(style->Name);
free(style->FontName);

track->parser_priv->override_marginbt[sid / 32] &= ~(1ul << (sid - 32 * (sid / 32)));
track->parser_priv->override_marginb[sid / 32] &= ~(1ul << (sid - 32 * (sid / 32)));
}

static int resize_read_order_bitmap(ASS_Track *track, int max_id)
Expand Down Expand Up @@ -234,7 +250,7 @@ static void set_default_style(ASS_Style *style)
style->Outline = 2;
style->Shadow = 3;
style->Alignment = 2;
style->MarginL = style->MarginR = style->MarginV = 20;
style->MarginL = style->MarginR = style->MarginV = style->MarginB = 20;
}

static long long string2timecode(ASS_Library *library, char *p)
Expand Down Expand Up @@ -372,13 +388,16 @@ static int process_event_tail(ASS_Track *track, ASS_Event *event,
*--end = 0;
}
event->Duration -= event->Start;
if (track->track_type != TRACK_TYPE_V4PP)
event->MarginB = event->MarginV;
free(format);
return event->Text ? 0 : -1; // "Text" is always the last
}
NEXT(p, token);

ALIAS(End, Duration) // temporarily store end timecode in event->Duration
ALIAS(Actor, Name) // both variants are used in files
ALIAS(MarginT, MarginV)
PARSE_START
INTVAL(Layer)
STYLEVAL(Style)
Expand All @@ -387,6 +406,7 @@ static int process_event_tail(ASS_Track *track, ASS_Event *event,
INTVAL(MarginL)
INTVAL(MarginR)
INTVAL(MarginV)
INTVAL(MarginB)
TIMEVAL(Start)
TIMEVAL(Duration)
PARSE_END
Expand Down Expand Up @@ -477,6 +497,21 @@ void ass_process_force_style(ASS_Track *track)
INTVAL(MarginL)
INTVAL(MarginR)
INTVAL(MarginV)
target->MarginB = target->MarginV;
} else if (ass_strcasecmp(tname, "MarginT") == 0) {
ASS_ParserPriv *priv = track->parser_priv;
size_t idx = sid / 32;
uint32_t bit = 1ul << (sid - 32 * idx);

priv->override_marginbt[idx] |= bit;
if (!(priv->override_marginb[idx] & bit))
target->MarginB = target->MarginV;
target->MarginV = atoi(token);
INTVAL(MarginB)
size_t idx = sid / 32;
uint32_t bit = 1ul << (sid - 32 * idx);
track->parser_priv->override_marginbt[idx] |= bit;
track->parser_priv->override_marginb[idx] |= bit;
INTVAL(Encoding)
FPVAL(ScaleX)
FPVAL(ScaleY)
Expand Down Expand Up @@ -549,6 +584,7 @@ static int process_style(ASS_Track *track, char *str)
NEXT(q, tname);
NEXT(p, token);

ALIAS(MarginT, MarginV)
PARSE_START
STARREDSTRVAL(Name)
STRVAL(FontName)
Expand Down Expand Up @@ -581,6 +617,7 @@ static int process_style(ASS_Track *track, char *str)
INTVAL(MarginL)
INTVAL(MarginR)
INTVAL(MarginV)
INTVAL(MarginB)
INTVAL(Encoding)
FPVAL(ScaleX)
FPVAL(ScaleY)
Expand All @@ -601,6 +638,8 @@ static int process_style(ASS_Track *track, char *str)
style->Italic = !!style->Italic;
style->Underline = !!style->Underline;
style->StrikeOut = !!style->StrikeOut;
if (track->track_type != TRACK_TYPE_V4PP)
target->MarginB = target->MarginV;
if (!style->Name)
style->Name = strdup("Default");
if (!style->FontName)
Expand Down
2 changes: 1 addition & 1 deletion libass/ass.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
#include <stdarg.h>
#include "ass_types.h"

#define LIBASS_VERSION 0x01600010
#define LIBASS_VERSION 0x01600020

#ifdef __cplusplus
extern "C" {
Expand Down
4 changes: 4 additions & 0 deletions libass/ass_priv.h
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ struct parser_priv {
uint32_t header_flags;

uint32_t feature_flags;

// bitmaps tracking style overrides for each style
uint32_t *override_marginbt;
uint32_t *override_marginb;
};

#endif /* LIBASS_PRIV_H */
15 changes: 12 additions & 3 deletions libass/ass_render.c
Original file line number Diff line number Diff line change
Expand Up @@ -960,6 +960,7 @@ static ASS_Style *handle_selective_style_overrides(ASS_Renderer *render_priv,
new->MarginL = user->MarginL;
new->MarginR = user->MarginR;
new->MarginV = user->MarginV;
new->MarginB = user->MarginB;
}

if (!new->FontName)
Expand Down Expand Up @@ -2797,8 +2798,16 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
(event->MarginL) ? event->MarginL : render_priv->state.style->MarginL;
int MarginR =
(event->MarginR) ? event->MarginR : render_priv->state.style->MarginR;
int MarginV =
int MarginT =
(event->MarginV) ? event->MarginV : render_priv->state.style->MarginV;
int MarginB;
uint32_t *om = render_priv->track->parser_priv->override_marginbt;
if (render_priv->track->track_type != TRACK_TYPE_V4PP &&
!(om[event->Style / 32] & (1 << (event->Style + 32 * (event->Style / 32)))))
MarginB = MarginT;
else
MarginB =
(event->MarginB) ? event->MarginB : render_priv->state.style->MarginB;

// calculate max length of a line
double max_text_width =
Expand Down Expand Up @@ -2870,7 +2879,7 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
if (valign == VALIGN_TOP) { // toptitle
device_y =
y2scr_top(render_priv,
MarginV) + text_info->lines[0].asc;
MarginT) + text_info->lines[0].asc;
} else if (valign == VALIGN_CENTER) { // midtitle
double scr_y =
y2scr(render_priv, render_priv->track->PlayResY / 2.0);
Expand All @@ -2884,7 +2893,7 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
"Invalid valign, assuming 0 (subtitle)");
scr_bottom =
y2scr_sub(render_priv,
render_priv->track->PlayResY - MarginV);
render_priv->track->PlayResY - MarginB);
scr_top = y2scr_top(render_priv, 0); //xxx not always 0?
device_y = scr_bottom + (scr_top - scr_bottom) * line_pos / 100.0;
device_y -= text_info->height;
Expand Down
2 changes: 0 additions & 2 deletions libass/ass_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ typedef struct ass_style {
and always only refer to the top margin regardless of format version */
int MarginV; // in SSA v4 and ASS (v4+) for both top and bottom, in v4++ only top
int MarginB; //v4++: bottom; v4 and v4+: identical to MarginV
// currently ignored, but should not be set to any value but zero
int Encoding;
int RelativeTo; // currently ignored, but should not be set to any value but 2
int treat_fontname_as_pattern; // does nothing (left in place for ABI-compatibility)
Expand All @@ -151,7 +150,6 @@ typedef struct ass_event {
and always only refer to the top margin regardless of format version */
int MarginV; // in SSA v4 and ASS (v4+) for both top and bottom, in v4++ only top
int MarginB; //v4++: bottom; v4 and v4+: identical to MarginV
// currently ignored, but should not be set to any value but zero
char *Effect;
char *Text;

Expand Down

0 comments on commit a1e3795

Please sign in to comment.