summaryrefslogtreecommitdiffstats
path: root/sub
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2013-03-30 20:08:56 +0100
committerwm4 <wm4@nowhere>2013-03-30 20:23:45 +0100
commitef3c0e6edafd6460d30c24dfd9f5b2f50401f38f (patch)
tree5b6b085430e3f200bfb033ca721c996e1d092c37 /sub
parentd39b131bde2f90c1607e1d9292c1a4ce291eb148 (diff)
downloadmpv-ef3c0e6edafd6460d30c24dfd9f5b2f50401f38f.tar.bz2
mpv-ef3c0e6edafd6460d30c24dfd9f5b2f50401f38f.tar.xz
osd: draw the OSD bar with ASS vector drawings
Drawing the bar with vector drawings (instead with characters from the OSD font) offers more flexibility and looks better. This also adds chapter marks to the OSD bar, which are visible as small triangles on the top and bottom inner border of the bar. Change the default position of the OSD bar below the center of the screen. This is less annoying than putting the bar directly into the center of the view, where it obscures the video. The new position is not quite on the bottom of the screen to avoid collisions with subtitles. The old centered position can be forced with ``--osd-bar-align-y=0``. Also make it possible to change the OSD bar width/height with the new --osd-bar-w and --osd-bar-h options. It's possible that the new OSD bar renders much slower than the old one. There are two reasons for this: 1. the character based bar allowed libass to cache each character, while the vector drawing forces it to redraw every time the bar position changes. 2., the bar position is updated at a much higher granularity (the bar position is passed along as float instead of as integer in the range 0-100, so the bar will be updated on every single video frame).
Diffstat (limited to 'sub')
-rw-r--r--sub/osd_libass.c230
-rw-r--r--sub/sub.h9
2 files changed, 172 insertions, 67 deletions
diff --git a/sub/osd_libass.c b/sub/osd_libass.c
index bbd925f135..9f5f1fd692 100644
--- a/sub/osd_libass.c
+++ b/sub/osd_libass.c
@@ -61,14 +61,19 @@ void osd_destroy_backend(struct osd_state *osd)
osd->osd_ass_library = NULL;
}
-static ASS_Track *create_osd_ass_track(struct osd_state *osd)
+static void create_osd_ass_track(struct osd_state *osd, struct osd_object *obj)
{
- ASS_Track *track = ass_new_track(osd->osd_ass_library);
+ ASS_Track *track = obj->osd_track;
+ if (!track)
+ track = ass_new_track(osd->osd_ass_library);
+
+ double aspect = 1.0 * obj->vo_res.w / FFMAX(obj->vo_res.h, 1) /
+ obj->vo_res.display_par;
track->track_type = TRACK_TYPE_ASS;
track->Timer = 100.;
track->PlayResY = MP_ASS_FONT_PLAYRESY;
- track->PlayResX = track->PlayResY * 1.33333;
+ track->PlayResX = track->PlayResY * aspect;
track->WrapStyle = 1; // end-of-line wrapping instead of smart wrapping
if (track->n_styles == 0) {
@@ -83,18 +88,19 @@ static ASS_Track *create_osd_ass_track(struct osd_state *osd)
style->Encoding = -1;
}
- return track;
+ obj->osd_track = track;
}
-static ASS_Event *get_osd_ass_event(ASS_Track *track)
+static ASS_Event *add_osd_ass_event(ASS_Track *track, const char *text)
{
- ass_flush_events(track);
- ass_alloc_event(track);
- ASS_Event *event = track->events + 0;
+ int n = ass_alloc_event(track);
+ ASS_Event *event = track->events + n;
event->Start = 0;
event->Duration = 100;
event->Style = track->default_style;
assert(event->Text == NULL);
+ if (text)
+ event->Text = strdup(text);
return event;
}
@@ -135,67 +141,135 @@ static char *mangle_ass(const char *in)
static void update_osd(struct osd_state *osd, struct osd_object *obj)
{
- if (!osd->osd_text[0]) {
- clear_obj(obj);
+ create_osd_ass_track(osd, obj);
+ clear_obj(obj);
+ if (!osd->osd_text[0])
return;
- }
- if (!obj->osd_track)
- obj->osd_track = create_osd_ass_track(osd);
- ASS_Event *event = get_osd_ass_event(obj->osd_track);
char *text = mangle_ass(osd->osd_text);
- event->Text = strdup(text);
+ add_osd_ass_event(obj->osd_track, text);
talloc_free(text);
}
-static int get_align(float val, int res, int *out_margin)
+// align: -1 .. +1
+// frame: size of the containing area
+// obj: size of the object that should be positioned inside the area
+// margin: min. distance from object to frame (as long as -1 <= align <= +1)
+static float get_align(float align, float frame, float obj, float margin)
{
- *out_margin = FFMAX(0, (1.0 - fabs(val)) * res / 2);
- if (fabs(val) < 0.1)
- return 1; // centered
- return val > 0 ? 2 : 0; // bottom / top (or right / left)
+ frame -= margin * 2;
+ return margin + frame / 2 - obj / 2 + (frame - obj) / 2 * align;
}
-static const int ass_align_x[3] = {1, 2, 3};
-static const int ass_align_y[3] = {4, 8, 0};
+struct ass_draw {
+ int scale;
+ char *text;
+};
-#define OSDBAR_ELEMS 46
+static void ass_draw_start(struct ass_draw *d)
+{
+ d->scale = FFMAX(d->scale, 1);
+ d->text = talloc_asprintf_append(d->text, "{\\p%d}", d->scale);
+}
-static void update_progbar(struct osd_state *osd, struct osd_object *obj)
+static void ass_draw_stop(struct ass_draw *d)
+{
+ d->text = talloc_strdup_append(d->text, "{\\p0}");
+}
+
+static void ass_draw_c(struct ass_draw *d, float x, float y)
+{
+ int ix = round(x * (1 << (d->scale - 1)));
+ int iy = round(y * (1 << (d->scale - 1)));
+ d->text = talloc_asprintf_append(d->text, " %d %d", ix, iy);
+}
+
+static void ass_draw_append(struct ass_draw *d, const char *t)
+{
+ d->text = talloc_strdup_append(d->text, t);
+}
+
+static void ass_draw_move_to(struct ass_draw *d, float x, float y)
+{
+ ass_draw_append(d, " m");
+ ass_draw_c(d, x, y);
+}
+
+static void ass_draw_line_to(struct ass_draw *d, float x, float y)
+{
+ ass_draw_append(d, " l");
+ ass_draw_c(d, x, y);
+}
+
+static void ass_draw_rect_ccw(struct ass_draw *d, float x0, float y0,
+ float x1, float y1)
+{
+ ass_draw_move_to(d, x0, y0);
+ ass_draw_line_to(d, x0, y1);
+ ass_draw_line_to(d, x1, y1);
+ ass_draw_line_to(d, x1, y0);
+}
+
+static void ass_draw_rect_cw(struct ass_draw *d, float x0, float y0,
+ float x1, float y1)
+{
+ ass_draw_move_to(d, x0, y0);
+ ass_draw_line_to(d, x1, y0);
+ ass_draw_line_to(d, x1, y1);
+ ass_draw_line_to(d, x0, y1);
+}
+
+static void ass_draw_reset(struct ass_draw *d)
+{
+ talloc_free(d->text);
+ d->text = NULL;
+}
+
+static void get_osd_bar_box(struct osd_state *osd, struct osd_object *obj,
+ float *o_x, float *o_y, float *o_w, float *o_h,
+ float *o_border)
{
struct MPOpts *opts = osd->opts;
- if (osd->progbar_type < 0) {
- clear_obj(obj);
- return;
+ bool new_track = !obj->osd_track;
+ create_osd_ass_track(osd, obj);
+ ASS_Track *track = obj->osd_track;
+ ASS_Style *style = track->styles + track->default_style;
+
+ *o_w = track->PlayResX * (opts->osd_bar_w / 100.0);
+ *o_h = track->PlayResY * (opts->osd_bar_h / 100.0);
+
+ if (new_track) {
+ float base_size = 0.03125;
+ style->Outline *= *o_h / track->PlayResY / base_size;
+ // So that the chapter marks have space between them
+ style->Outline = FFMIN(style->Outline, *o_h / 5.2);
+ // So that the border is not 0
+ style->Outline = FFMAX(style->Outline, *o_h / 32.0);
+ // Rendering with shadow is broken (because there's more than one shape)
+ style->Shadow = 0;
}
- if (!obj->osd_track)
- obj->osd_track = create_osd_ass_track(osd);
+ *o_border = style->Outline;
- ASS_Style *style = obj->osd_track->styles + obj->osd_track->default_style;
+ *o_x = get_align(opts->osd_bar_align_x, track->PlayResX, *o_w, *o_border);
+ *o_y = get_align(opts->osd_bar_align_y, track->PlayResY, *o_h, *o_border);
+}
+
+static void update_progbar(struct osd_state *osd, struct osd_object *obj)
+{
+ float px, py, width, height, border;
+ get_osd_bar_box(osd, obj, &px, &py, &width, &height, &border);
- int ax = get_align(opts->osd_bar_align_x, obj->osd_track->PlayResX,
- &style->MarginR);
- int ay = get_align(opts->osd_bar_align_y, obj->osd_track->PlayResY,
- &style->MarginV);
- style->Alignment = ass_align_x[ax] + ass_align_y[ay];
- style->MarginL = style->MarginR;
+ clear_obj(obj);
- // We need a fixed font size with respect to the OSD width.
- // Assume the OSD bar takes 2/3 of the OSD width at PlayResY=288 and
- // FontSize=22 with an OSD aspect ratio of 16:9. Rescale as needed.
- // xxx can fail when unknown fonts are involved
- double asp = (double)obj->vo_res.w / obj->vo_res.h;
- double scale = (asp / 1.77777) * (obj->osd_track->PlayResY / 288.0);
- style->ScaleX = style->ScaleY = scale;
- style->FontSize = 22.0;
- style->Outline = style->FontSize / 16 * scale;
+ if (osd->progbar_type < 0)
+ return;
- int active = (osd->progbar_value * OSDBAR_ELEMS + 255) / 256;
- active = FFMIN(OSDBAR_ELEMS, FFMAX(active, 0));
+ float sx = px - border * 2 - height / 4; // includes additional spacing
+ float sy = py + height / 2;
- char *text = talloc_strdup(NULL, "{\\q2}");
+ char *text = talloc_asprintf(NULL, "{\\an6\\pos(%f,%f)}", sx, sy);
if (osd->progbar_type == 0 || osd->progbar_type >= 256) {
// no sym
@@ -207,30 +281,57 @@ static void update_progbar(struct osd_state *osd, struct osd_object *obj)
text = talloc_strdup_append_buffer(text, "{\\r}");
}
- //xxx space in normal font, because OSD font doesn't have a space
- text = talloc_strdup_append_buffer(text, "\\h");
- text = talloc_strdup_append_buffer(text, ASS_USE_OSD_FONT);
+ add_osd_ass_event(obj->osd_track, text);
+ talloc_free(text);
- text = mp_append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_START);
- for (int n = 0; n < active; n++)
- text = mp_append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_0);
- for (int n = 0; n < OSDBAR_ELEMS - active; n++)
- text = mp_append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_1);
- text = mp_append_utf8_buffer(text, OSD_CODEPOINTS + OSD_PB_END);
+ struct ass_draw *d = &(struct ass_draw) { .scale = 4 };
+ // filled area
+ d->text = talloc_asprintf_append(d->text, "{\\pos(%f,%f)}", px, py);
+ ass_draw_start(d);
+ float pos = osd->progbar_value * width - border / 2;
+ ass_draw_rect_cw(d, 0, 0, pos, height);
+ ass_draw_stop(d);
+ add_osd_ass_event(obj->osd_track, d->text);
+ ass_draw_reset(d);
+
+ d->text = talloc_asprintf_append(d->text, "{\\pos(%f,%f)}", px, py);
+ ass_draw_start(d);
+
+ // the box
+ ass_draw_rect_cw(d, -border, -border, width + border, height + border);
+
+ // the "hole"
+ ass_draw_rect_ccw(d, 0, 0, width, height);
+
+ // chapter marks
+ for (int n = 0; n < osd->progbar_num_stops; n++) {
+ float s = osd->progbar_stops[n] * width;
+ float dent = border * 1.3;
+
+ if (s > dent && s < width - dent) {
+ ass_draw_move_to(d, s + dent, 0);
+ ass_draw_line_to(d, s, dent);
+ ass_draw_line_to(d, s - dent, 0);
+
+ ass_draw_move_to(d, s - dent, height);
+ ass_draw_line_to(d, s, height - dent);
+ ass_draw_line_to(d, s + dent, height);
+ }
+ }
- ASS_Event *event = get_osd_ass_event(obj->osd_track);
- event->Text = strdup(text);
- talloc_free(text);
+ ass_draw_stop(d);
+ add_osd_ass_event(obj->osd_track, d->text);
+ ass_draw_reset(d);
}
static void update_sub(struct osd_state *osd, struct osd_object *obj)
{
struct MPOpts *opts = osd->opts;
- if (!(vo_sub && opts->sub_visibility)) {
- clear_obj(obj);
+ clear_obj(obj);
+
+ if (!(vo_sub && opts->sub_visibility))
return;
- }
if (!obj->osd_track)
obj->osd_track = mp_ass_default_track(osd->osd_ass_library, osd->opts);
@@ -250,9 +351,8 @@ static void update_sub(struct osd_state *osd, struct osd_object *obj)
for (int n = 0; n < vo_sub->lines; n++)
text = talloc_asprintf_append_buffer(text, "%s\n", vo_sub->text[n]);
- ASS_Event *event = get_osd_ass_event(obj->osd_track);
char *escaped_text = mangle_ass(text);
- event->Text = strdup(escaped_text);
+ add_osd_ass_event(obj->osd_track, escaped_text);
talloc_free(escaped_text);
talloc_free(text);
}
diff --git a/sub/sub.h b/sub/sub.h
index 2055799d72..660fd9ccc9 100644
--- a/sub/sub.h
+++ b/sub/sub.h
@@ -129,8 +129,13 @@ struct osd_state {
bool want_redraw;
- char *osd_text; // OSDTYPE_OSD
- int progbar_type, progbar_value; // OSDTYPE_PROGBAR
+ // OSDTYPE_OSD
+ char *osd_text;
+ // OSDTYPE_PROGBAR
+ int progbar_type; // <0: disabled, 1-255: symbol, else: no symbol
+ float progbar_value; // range 0.0-1.0
+ float *progbar_stops; // used for chapter indicators (0.0-1.0 each)
+ int progbar_num_stops;
int switch_sub_id;