From 9aea06cf7dde9592672e4dba09fb03b2b89d6863 Mon Sep 17 00:00:00 2001 From: greg Date: Fri, 8 Jan 2010 18:35:44 +0000 Subject: Update internal libass copy to commit 8db4a5 git-svn-id: svn://svn.mplayerhq.hu/mplayer/trunk@30242 b3059339-0415-0410-9bf9-f77b7e298cf2 --- Makefile | 3 + libass/ass.c | 1804 +++++++++-------- libass/ass.h | 307 ++- libass/ass_bitmap.c | 720 ++++--- libass/ass_bitmap.h | 30 +- libass/ass_cache.c | 440 ++-- libass/ass_cache.h | 190 +- libass/ass_cache_template.h | 122 ++ libass/ass_drawing.c | 495 +++++ libass/ass_drawing.h | 77 + libass/ass_font.c | 673 ++++--- libass/ass_font.h | 60 +- libass/ass_fontconfig.c | 751 ++++--- libass/ass_fontconfig.h | 20 +- libass/ass_library.c | 146 +- libass/ass_library.h | 30 +- libass/ass_parse.c | 926 +++++++++ libass/ass_parse.h | 38 + libass/ass_render.c | 4661 ++++++++++++++++++++++--------------------- libass/ass_render.h | 262 +++ libass/ass_strtod.c | 247 +++ libass/ass_types.h | 172 +- libass/ass_utils.c | 236 ++- libass/ass_utils.h | 139 +- 24 files changed, 7767 insertions(+), 4782 deletions(-) create mode 100644 libass/ass_cache_template.h create mode 100644 libass/ass_drawing.c create mode 100644 libass/ass_drawing.h create mode 100644 libass/ass_parse.c create mode 100644 libass/ass_parse.h create mode 100644 libass/ass_render.h create mode 100644 libass/ass_strtod.c diff --git a/Makefile b/Makefile index 46069a7d8b..00d615bfdb 100644 --- a/Makefile +++ b/Makefile @@ -122,10 +122,13 @@ SRCS_COMMON-$(LIBASS) += libmpcodecs/vf_ass.c \ SRCS_COMMON-$(LIBASS_INTERNAL) += libass/ass.c \ libass/ass_bitmap.c \ libass/ass_cache.c \ + libass/ass_drawing.c \ libass/ass_font.c \ libass/ass_fontconfig.c \ libass/ass_library.c \ + libass/ass_parse.c \ libass/ass_render.c \ + libass/ass_strtod.c \ libass/ass_utils.c \ SRCS_COMMON-$(LIBAVCODEC) += av_opts.c \ diff --git a/libass/ass.c b/libass/ass.c index 370063aacf..6becb39e8e 100644 --- a/libass/ass.c +++ b/libass/ass.c @@ -1,5 +1,3 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: /* * Copyright (C) 2006 Evgeniy Stepanov * @@ -39,117 +37,137 @@ #include "ass.h" #include "ass_utils.h" #include "ass_library.h" -#include "mputils.h" -typedef enum {PST_UNKNOWN = 0, PST_INFO, PST_STYLES, PST_EVENTS, PST_FONTS} parser_state_t; - -struct parser_priv_s { - parser_state_t state; - char* fontname; - char* fontdata; - int fontdata_size; - int fontdata_used; +typedef enum { + PST_UNKNOWN = 0, + PST_INFO, + PST_STYLES, + PST_EVENTS, + PST_FONTS +} ParserState; + +struct parser_priv { + ParserState state; + char *fontname; + char *fontdata; + int fontdata_size; + int fontdata_used; }; #define ASS_STYLES_ALLOC 20 #define ASS_EVENTS_ALLOC 200 -void ass_free_track(ass_track_t* track) { - int i; - - if (track->parser_priv) { - if (track->parser_priv->fontname) - free(track->parser_priv->fontname); - if (track->parser_priv->fontdata) - free(track->parser_priv->fontdata); - free(track->parser_priv); - } - if (track->style_format) - free(track->style_format); - if (track->event_format) - free(track->event_format); - if (track->styles) { - for (i = 0; i < track->n_styles; ++i) - ass_free_style(track, i); - free(track->styles); - } - if (track->events) { - for (i = 0; i < track->n_events; ++i) - ass_free_event(track, i); - free(track->events); - } +void ass_free_track(ASS_Track *track) +{ + int i; + + if (track->parser_priv) { + if (track->parser_priv->fontname) + free(track->parser_priv->fontname); + if (track->parser_priv->fontdata) + free(track->parser_priv->fontdata); + free(track->parser_priv); + } + if (track->style_format) + free(track->style_format); + if (track->event_format) + free(track->event_format); + if (track->styles) { + for (i = 0; i < track->n_styles; ++i) + ass_free_style(track, i); + free(track->styles); + } + if (track->events) { + for (i = 0; i < track->n_events; ++i) + ass_free_event(track, i); + free(track->events); + } + free(track->name); + free(track); } /// \brief Allocate a new style struct /// \param track track /// \return style id -int ass_alloc_style(ass_track_t* track) { - int sid; +int ass_alloc_style(ASS_Track *track) +{ + int sid; - assert(track->n_styles <= track->max_styles); + assert(track->n_styles <= track->max_styles); - if (track->n_styles == track->max_styles) { - track->max_styles += ASS_STYLES_ALLOC; - track->styles = (ass_style_t*)realloc(track->styles, sizeof(ass_style_t)*track->max_styles); - } + if (track->n_styles == track->max_styles) { + track->max_styles += ASS_STYLES_ALLOC; + track->styles = + (ASS_Style *) realloc(track->styles, + sizeof(ASS_Style) * + track->max_styles); + } - sid = track->n_styles++; - memset(track->styles + sid, 0, sizeof(ass_style_t)); - return sid; + sid = track->n_styles++; + memset(track->styles + sid, 0, sizeof(ASS_Style)); + return sid; } /// \brief Allocate a new event struct /// \param track track /// \return event id -int ass_alloc_event(ass_track_t* track) { - int eid; +int ass_alloc_event(ASS_Track *track) +{ + int eid; - assert(track->n_events <= track->max_events); + assert(track->n_events <= track->max_events); - if (track->n_events == track->max_events) { - track->max_events += ASS_EVENTS_ALLOC; - track->events = (ass_event_t*)realloc(track->events, sizeof(ass_event_t)*track->max_events); - } + if (track->n_events == track->max_events) { + track->max_events += ASS_EVENTS_ALLOC; + track->events = + (ASS_Event *) realloc(track->events, + sizeof(ASS_Event) * + track->max_events); + } - eid = track->n_events++; - memset(track->events + eid, 0, sizeof(ass_event_t)); - return eid; + eid = track->n_events++; + memset(track->events + eid, 0, sizeof(ASS_Event)); + return eid; } -void ass_free_event(ass_track_t* track, int eid) { - ass_event_t* event = track->events + eid; - if (event->Name) - free(event->Name); - if (event->Effect) - free(event->Effect); - if (event->Text) - free(event->Text); - if (event->render_priv) - free(event->render_priv); +void ass_free_event(ASS_Track *track, int eid) +{ + ASS_Event *event = track->events + eid; + if (event->Name) + free(event->Name); + if (event->Effect) + free(event->Effect); + if (event->Text) + free(event->Text); + if (event->render_priv) + free(event->render_priv); } -void ass_free_style(ass_track_t* track, int sid) { - ass_style_t* style = track->styles + sid; - if (style->Name) - free(style->Name); - if (style->FontName) - free(style->FontName); +void ass_free_style(ASS_Track *track, int sid) +{ + ASS_Style *style = track->styles + sid; + if (style->Name) + free(style->Name); + if (style->FontName) + free(style->FontName); } // ============================================================================================== -static void skip_spaces(char** str) { - char* p = *str; - while ((*p==' ') || (*p=='\t')) - ++p; - *str = p; +static void skip_spaces(char **str) +{ + char *p = *str; + while ((*p == ' ') || (*p == '\t')) + ++p; + *str = p; } -static void rskip_spaces(char** str, char* limit) { - char* p = *str; - while ((p >= limit) && ((*p==' ') || (*p=='\t'))) - --p; - *str = p; +static void rskip_spaces(char **str, char *limit) +{ + char *p = *str; + while ((p >= limit) && ((*p == ' ') || (*p == '\t'))) + --p; + *str = p; } /** @@ -160,47 +178,55 @@ static void rskip_spaces(char** str, char* limit) { * Returnes 0 if no styles found => expects at least 1 style. * Parsing code always adds "Default" style in the end. */ -static int lookup_style(ass_track_t* track, char* name) { - int i; - if (*name == '*') ++name; // FIXME: what does '*' really mean ? - for (i = track->n_styles - 1; i >= 0; --i) { - // FIXME: mb strcasecmp ? - if (strcmp(track->styles[i].Name, name) == 0) - return i; - } - i = track->default_style; - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NoStyleNamedXFoundUsingY, track, name, track->styles[i].Name); - return i; // use the first style +static int lookup_style(ASS_Track *track, char *name) +{ + int i; + if (*name == '*') + ++name; // FIXME: what does '*' really mean ? + for (i = track->n_styles - 1; i >= 0; --i) { + // FIXME: mb strcasecmp ? + if (strcmp(track->styles[i].Name, name) == 0) + return i; + } + i = track->default_style; + ass_msg(track->library, MSGL_WARN, + "[%p]: Warning: no style named '%s' found, using '%s'", + track, name, track->styles[i].Name); + return i; // use the first style } -static uint32_t string2color(char* p) { - uint32_t tmp; - (void)strtocolor(&p, &tmp); - return tmp; +static uint32_t string2color(ASS_Library *library, char *p) +{ + uint32_t tmp; + (void) strtocolor(library, &p, &tmp, 0); + return tmp; } -static long long string2timecode(char* p) { - unsigned h, m, s, ms; - long long tm; - int res = sscanf(p, "%1d:%2d:%2d.%2d", &h, &m, &s, &ms); - if (res < 4) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_BadTimestamp); - return 0; - } - tm = ((h * 60 + m) * 60 + s) * 1000 + ms * 10; - return tm; +static long long string2timecode(ASS_Library *library, char *p) +{ + unsigned h, m, s, ms; + long long tm; + int res = sscanf(p, "%1d:%2d:%2d.%2d", &h, &m, &s, &ms); + if (res < 4) { + ass_msg(library, MSGL_WARN, "Bad timestamp"); + return 0; + } + tm = ((h * 60 + m) * 60 + s) * 1000 + ms * 10; + return tm; } /** * \brief converts numpad-style align to align. */ -static int numpad2align(int val) { - int res, v; - v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment - if (v != 0) v = 3 - v; - res = ((val - 1) % 3) + 1; // horizontal alignment - res += v*4; - return res; +static int numpad2align(int val) +{ + int res, v; + v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment + if (v != 0) + v = 3 - v; + res = ((val - 1) % 3) + 1; // horizontal alignment + res += v * 4; + return res; } #define NEXT(str,token) \ @@ -210,51 +236,62 @@ static int numpad2align(int val) { #define ANYVAL(name,func) \ } else if (strcasecmp(tname, #name) == 0) { \ target->name = func(token); \ - mp_msg(MSGT_ASS, MSGL_DBG2, "%s = %s\n", #name, token); + ass_msg(track->library, MSGL_DBG2, "%s = %s", #name, token); #define STRVAL(name) \ } else if (strcasecmp(tname, #name) == 0) { \ if (target->name != NULL) free(target->name); \ target->name = strdup(token); \ - mp_msg(MSGT_ASS, MSGL_DBG2, "%s = %s\n", #name, token); + ass_msg(track->library, MSGL_DBG2, "%s = %s", #name, token); + +#define COLORVAL(name) \ + } else if (strcasecmp(tname, #name) == 0) { \ + target->name = string2color(track->library, token); \ + ass_msg(track->library, MSGL_DBG2, "%s = %s", #name, token); -#define COLORVAL(name) ANYVAL(name,string2color) #define INTVAL(name) ANYVAL(name,atoi) #define FPVAL(name) ANYVAL(name,atof) -#define TIMEVAL(name) ANYVAL(name,string2timecode) +#define TIMEVAL(name) \ + } else if (strcasecmp(tname, #name) == 0) { \ + target->name = string2timecode(track->library, token); \ + ass_msg(track->library, MSGL_DBG2, "%s = %s", #name, token); + #define STYLEVAL(name) \ } else if (strcasecmp(tname, #name) == 0) { \ target->name = lookup_style(track, token); \ - mp_msg(MSGT_ASS, MSGL_DBG2, "%s = %s\n", #name, token); + ass_msg(track->library, MSGL_DBG2, "%s = %s", #name, token); #define ALIAS(alias,name) \ if (strcasecmp(tname, #alias) == 0) {tname = #name;} -static char* next_token(char** str) { - char* p = *str; - char* start; - skip_spaces(&p); - if (*p == '\0') { - *str = p; - return 0; - } - start = p; // start of the token - for (; (*p != '\0') && (*p != ','); ++p) {} - if (*p == '\0') { - *str = p; // eos found, str will point to '\0' at exit - } else { - *p = '\0'; - *str = p + 1; // ',' found, str will point to the next char (beginning of the next token) - } - --p; // end of current token - rskip_spaces(&p, start); - if (p < start) - p = start; // empty token - else - ++p; // the first space character, or '\0' - *p = '\0'; - return start; +static char *next_token(char **str) +{ + char *p = *str; + char *start; + skip_spaces(&p); + if (*p == '\0') { + *str = p; + return 0; + } + start = p; // start of the token + for (; (*p != '\0') && (*p != ','); ++p) { + } + if (*p == '\0') { + *str = p; // eos found, str will point to '\0' at exit + } else { + *p = '\0'; + *str = p + 1; // ',' found, str will point to the next char (beginning of the next token) + } + --p; // end of current token + rskip_spaces(&p, start); + if (p < start) + p = start; // empty token + else + ++p; // the first space character, or '\0' + *p = '\0'; + return start; } + /** * \brief Parse the tail of Dialogue line * \param track track @@ -262,68 +299,62 @@ static char* next_token(char** str) { * \param str string to parse, zero-terminated * \param n_ignored number of format options to skip at the beginning */ -static int process_event_tail(ass_track_t* track, ass_event_t* event, char* str, int n_ignored) +static int process_event_tail(ASS_Track *track, ASS_Event *event, + char *str, int n_ignored) { - char* token; - char* tname; - char* p = str; - int i; - ass_event_t* target = event; - - char* format; - char* q; // format scanning pointer - - if (!track->event_format) { - track->event_format = strdup("Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text"); - mp_msg(MSGT_ASS, MSGL_V, "Event format is broken, reseting to defaults.\n"); - } - - q = format = strdup(track->event_format); - - if (track->n_styles == 0) { - // add "Default" style to the end - // will be used if track does not contain a default style (or even does not contain styles at all) - int sid = ass_alloc_style(track); - track->styles[sid].Name = strdup("Default"); - track->styles[sid].FontName = strdup("Arial"); - } - - for (i = 0; i < n_ignored; ++i) { - NEXT(q, tname); - } - - while (1) { - NEXT(q, tname); - if (strcasecmp(tname, "Text") == 0) { - char* last; - event->Text = strdup(p); - if (*event->Text != 0) { - last = event->Text + strlen(event->Text) - 1; - if (last >= event->Text && *last == '\r') - *last = 0; - } - mp_msg(MSGT_ASS, MSGL_DBG2, "Text = %s\n", event->Text); - event->Duration -= event->Start; - free(format); - return 0; // "Text" is always the last - } - NEXT(p, token); - - ALIAS(End,Duration) // temporarily store end timecode in event->Duration - if (0) { // cool ;) - INTVAL(Layer) - STYLEVAL(Style) - STRVAL(Name) - STRVAL(Effect) - INTVAL(MarginL) - INTVAL(MarginR) - INTVAL(MarginV) - TIMEVAL(Start) - TIMEVAL(Duration) - } - } - free(format); - return 1; + char *token; + char *tname; + char *p = str; + int i; + ASS_Event *target = event; + + char *format = strdup(track->event_format); + char *q = format; // format scanning pointer + + if (track->n_styles == 0) { + // add "Default" style to the end + // will be used if track does not contain a default style (or even does not contain styles at all) + int sid = ass_alloc_style(track); + track->styles[sid].Name = strdup("Default"); + track->styles[sid].FontName = strdup("Arial"); + } + + for (i = 0; i < n_ignored; ++i) { + NEXT(q, tname); + } + + while (1) { + NEXT(q, tname); + if (strcasecmp(tname, "Text") == 0) { + char *last; + event->Text = strdup(p); + if (*event->Text != 0) { + last = event->Text + strlen(event->Text) - 1; + if (last >= event->Text && *last == '\r') + *last = 0; + } + ass_msg(track->library, MSGL_DBG2, "Text = %s", event->Text); + event->Duration -= event->Start; + free(format); + return 0; // "Text" is always the last + } + NEXT(p, token); + + ALIAS(End, Duration) // temporarily store end timecode in event->Duration + if (0) { // cool ;) + INTVAL(Layer) + STYLEVAL(Style) + STRVAL(Name) + STRVAL(Effect) + INTVAL(MarginL) + INTVAL(MarginR) + INTVAL(MarginV) + TIMEVAL(Start) + TIMEVAL(Duration) + } + } + free(format); + return 1; } /** @@ -331,73 +362,79 @@ static int process_event_tail(ass_track_t* track, ass_event_t* event, char* str, * \param track track to apply overrides to * The format for overrides is [StyleName.]Field=Value */ -void process_force_style(ass_track_t* track) { - char **fs, *eq, *dt, *style, *tname, *token; - ass_style_t* target; - int sid; - char** list = track->library->style_overrides; - - if (!list) return; - - for (fs = list; *fs; ++fs) { - eq = strrchr(*fs, '='); - if (!eq) - continue; - *eq = '\0'; - token = eq + 1; - - if(!strcasecmp(*fs, "PlayResX")) - track->PlayResX = atoi(token); - else if(!strcasecmp(*fs, "PlayResY")) - track->PlayResY = atoi(token); - else if(!strcasecmp(*fs, "Timer")) - track->Timer = atof(token); - else if(!strcasecmp(*fs, "WrapStyle")) - track->WrapStyle = atoi(token); - else if(!strcasecmp(*fs, "ScaledBorderAndShadow")) - track->ScaledBorderAndShadow = parse_bool(token); - - dt = strrchr(*fs, '.'); - if (dt) { - *dt = '\0'; - style = *fs; - tname = dt + 1; - } else { - style = NULL; - tname = *fs; - } - for (sid = 0; sid < track->n_styles; ++sid) { - if (style == NULL || strcasecmp(track->styles[sid].Name, style) == 0) { - target = track->styles + sid; - if (0) { - STRVAL(FontName) - COLORVAL(PrimaryColour) - COLORVAL(SecondaryColour) - COLORVAL(OutlineColour) - COLORVAL(BackColour) - FPVAL(FontSize) - INTVAL(Bold) - INTVAL(Italic) - INTVAL(Underline) - INTVAL(StrikeOut) - FPVAL(Spacing) - INTVAL(Angle) - INTVAL(BorderStyle) - INTVAL(Alignment) - INTVAL(MarginL) - INTVAL(MarginR) - INTVAL(MarginV) - INTVAL(Encoding) - FPVAL(ScaleX) - FPVAL(ScaleY) - FPVAL(Outline) - FPVAL(Shadow) - } - } - } - *eq = '='; - if (dt) *dt = '.'; - } +void ass_process_force_style(ASS_Track *track) +{ + char **fs, *eq, *dt, *style, *tname, *token; + ASS_Style *target; + int sid; + char **list = track->library->style_overrides; + + if (!list) + return; + + for (fs = list; *fs; ++fs) { + eq = strrchr(*fs, '='); + if (!eq) + continue; + *eq = '\0'; + token = eq + 1; + + if (!strcasecmp(*fs, "PlayResX")) + track->PlayResX = atoi(token); + else if (!strcasecmp(*fs, "PlayResY")) + track->PlayResY = atoi(token); + else if (!strcasecmp(*fs, "Timer")) + track->Timer = atof(token); + else if (!strcasecmp(*fs, "WrapStyle")) + track->WrapStyle = atoi(token); + else if (!strcasecmp(*fs, "ScaledBorderAndShadow")) + track->ScaledBorderAndShadow = parse_bool(token); + else if (!strcasecmp(*fs, "Kerning")) + track->Kerning = parse_bool(token); + + dt = strrchr(*fs, '.'); + if (dt) { + *dt = '\0'; + style = *fs; + tname = dt + 1; + } else { + style = NULL; + tname = *fs; + } + for (sid = 0; sid < track->n_styles; ++sid) { + if (style == NULL + || strcasecmp(track->styles[sid].Name, style) == 0) { + target = track->styles + sid; + if (0) { + STRVAL(FontName) + COLORVAL(PrimaryColour) + COLORVAL(SecondaryColour) + COLORVAL(OutlineColour) + COLORVAL(BackColour) + FPVAL(FontSize) + INTVAL(Bold) + INTVAL(Italic) + INTVAL(Underline) + INTVAL(StrikeOut) + FPVAL(Spacing) + INTVAL(Angle) + INTVAL(BorderStyle) + INTVAL(Alignment) + INTVAL(MarginL) + INTVAL(MarginR) + INTVAL(MarginV) + INTVAL(Encoding) + FPVAL(ScaleX) + FPVAL(ScaleY) + FPVAL(Outline) + FPVAL(Shadow) + } + } + } + *eq = '='; + if (dt) + *dt = '.'; + } } /** @@ -406,257 +443,294 @@ void process_force_style(ass_track_t* track) { * \param str string to parse, zero-terminated * Allocates a new style struct. */ -static int process_style(ass_track_t* track, char *str) +static int process_style(ASS_Track *track, char *str) { - char* token; - char* tname; - char* p = str; - char* format; - char* q; // format scanning pointer - int sid; - ass_style_t* style; - ass_style_t* target; - - if (!track->style_format) { - // no style format header - // probably an ancient script version - if (track->track_type == TRACK_TYPE_SSA) - track->style_format = strdup("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour," - "TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline," - "Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding"); - else - track->style_format = strdup("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour," - "OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut," - "ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow," - "Alignment, MarginL, MarginR, MarginV, Encoding"); - } - - q = format = strdup(track->style_format); - - mp_msg(MSGT_ASS, MSGL_V, "[%p] Style: %s\n", track, str); - - sid = ass_alloc_style(track); - - style = track->styles + sid; - target = style; -// fill style with some default values - style->ScaleX = 100.; - style->ScaleY = 100.; - - while (1) { - NEXT(q, tname); - NEXT(p, token); - -// ALIAS(TertiaryColour,OutlineColour) // ignore TertiaryColour; it appears only in SSA, and is overridden by BackColour - - if (0) { // cool ;) - STRVAL(Name) - if ((strcmp(target->Name, "Default")==0) || (strcmp(target->Name, "*Default")==0)) - track->default_style = sid; - STRVAL(FontName) - COLORVAL(PrimaryColour) - COLORVAL(SecondaryColour) - COLORVAL(OutlineColour) // TertiaryColor - COLORVAL(BackColour) - // SSA uses BackColour for both outline and shadow - // this will destroy SSA's TertiaryColour, but i'm not going to use it anyway - if (track->track_type == TRACK_TYPE_SSA) - target->OutlineColour = target->BackColour; - FPVAL(FontSize) - INTVAL(Bold) - INTVAL(Italic) - INTVAL(Underline) - INTVAL(StrikeOut) - FPVAL(Spacing) - INTVAL(Angle) - INTVAL(BorderStyle) - INTVAL(Alignment) - if (track->track_type == TRACK_TYPE_ASS) - target->Alignment = numpad2align(target->Alignment); - INTVAL(MarginL) - INTVAL(MarginR) - INTVAL(MarginV) - INTVAL(Encoding) - FPVAL(ScaleX) - FPVAL(ScaleY) - FPVAL(Outline) - FPVAL(Shadow) - } - } - style->ScaleX /= 100.; - style->ScaleY /= 100.; - style->Bold = !!style->Bold; - style->Italic = !!style->Italic; - style->Underline = !!style->Underline; - if (!style->Name) - style->Name = strdup("Default"); - if (!style->FontName) - style->FontName = strdup("Arial"); - // skip '@' at the start of the font name - if (*style->FontName == '@') { - p = style->FontName; - style->FontName = strdup(p + 1); - free(p); - } - free(format); - return 0; + char *token; + char *tname; + char *p = str; + char *format; + char *q; // format scanning pointer + int sid; + ASS_Style *style; + ASS_Style *target; + + if (!track->style_format) { + // no style format header + // probably an ancient script version + if (track->track_type == TRACK_TYPE_SSA) + track->style_format = + strdup + ("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour," + "TertiaryColour, BackColour, Bold, Italic, BorderStyle, Outline," + "Shadow, Alignment, MarginL, MarginR, MarginV, AlphaLevel, Encoding"); + else + track->style_format = + strdup + ("Name, Fontname, Fontsize, PrimaryColour, SecondaryColour," + "OutlineColour, BackColour, Bold, Italic, Underline, StrikeOut," + "ScaleX, ScaleY, Spacing, Angle, BorderStyle, Outline, Shadow," + "Alignment, MarginL, MarginR, MarginV, Encoding"); + } + + q = format = strdup(track->style_format); + + ass_msg(track->library, MSGL_V, "[%p] Style: %s", track, str); + + sid = ass_alloc_style(track); + + style = track->styles + sid; + target = style; + + // fill style with some default values + style->ScaleX = 100.; + style->ScaleY = 100.; + + while (1) { + NEXT(q, tname); + NEXT(p, token); + + if (0) { // cool ;) + STRVAL(Name) + if ((strcmp(target->Name, "Default") == 0) + || (strcmp(target->Name, "*Default") == 0)) + track->default_style = sid; + STRVAL(FontName) + COLORVAL(PrimaryColour) + COLORVAL(SecondaryColour) + COLORVAL(OutlineColour) // TertiaryColor + COLORVAL(BackColour) + // SSA uses BackColour for both outline and shadow + // this will destroy SSA's TertiaryColour, but i'm not going to use it anyway + if (track->track_type == TRACK_TYPE_SSA) + target->OutlineColour = target->BackColour; + FPVAL(FontSize) + INTVAL(Bold) + INTVAL(Italic) + INTVAL(Underline) + INTVAL(StrikeOut) + FPVAL(Spacing) + INTVAL(Angle) + INTVAL(BorderStyle) + INTVAL(Alignment) + if (track->track_type == TRACK_TYPE_ASS) + target->Alignment = numpad2align(target->Alignment); + INTVAL(MarginL) + INTVAL(MarginR) + INTVAL(MarginV) + INTVAL(Encoding) + FPVAL(ScaleX) + FPVAL(ScaleY) + FPVAL(Outline) + FPVAL(Shadow) + } + } + style->ScaleX /= 100.; + style->ScaleY /= 100.; + style->Bold = !!style->Bold; + style->Italic = !!style->Italic; + style->Underline = !!style->Underline; + if (!style->Name) + style->Name = strdup("Default"); + if (!style->FontName) + style->FontName = strdup("Arial"); + // skip '@' at the start of the font name + if (*style->FontName == '@') { + p = style->FontName; + style->FontName = strdup(p + 1); + free(p); + } + free(format); + return 0; } -static int process_styles_line(ass_track_t* track, char *str) +static int process_styles_line(ASS_Track *track, char *str) { - if (!strncmp(str,"Format:", 7)) { - char* p = str + 7; - skip_spaces(&p); - track->style_format = strdup(p); - mp_msg(MSGT_ASS, MSGL_DBG2, "Style format: %s\n", track->style_format); - } else if (!strncmp(str,"Style:", 6)) { - char* p = str + 6; - skip_spaces(&p); - process_style(track, p); - } - return 0; + if (!strncmp(str, "Format:", 7)) { + char *p = str + 7; + skip_spaces(&p); + track->style_format = strdup(p); + ass_msg(track->library, MSGL_DBG2, "Style format: %s", + track->style_format); + } else if (!strncmp(str, "Style:", 6)) { + char *p = str + 6; + skip_spaces(&p); + process_style(track, p); + } + return 0; } -static int process_info_line(ass_track_t* track, char *str) +static int process_info_line(ASS_Track *track, char *str) { - if (!strncmp(str, "PlayResX:", 9)) { - track->PlayResX = atoi(str + 9); - } else if (!strncmp(str,"PlayResY:", 9)) { - track->PlayResY = atoi(str + 9); - } else if (!strncmp(str,"Timer:", 6)) { - track->Timer = atof(str + 6); - } else if (!strncmp(str,"WrapStyle:", 10)) { - track->WrapStyle = atoi(str + 10); - } else if (!strncmp(str, "ScaledBorderAndShadow:", 22)) { - track->ScaledBorderAndShadow = parse_bool(str + 22); - } - return 0; + if (!strncmp(str, "PlayResX:", 9)) { + track->PlayResX = atoi(str + 9); + } else if (!strncmp(str, "PlayResY:", 9)) { + track->PlayResY = atoi(str + 9); + } else if (!strncmp(str, "Timer:", 6)) { + track->Timer = atof(str + 6); + } else if (!strncmp(str, "WrapStyle:", 10)) { + track->WrapStyle = atoi(str + 10); + } else if (!strncmp(str, "ScaledBorderAndShadow:", 22)) { + track->ScaledBorderAndShadow = parse_bool(str + 22); + } else if (!strncmp(str, "Kerning:", 8)) { + track->Kerning = parse_bool(str + 8); + } + return 0; } -static int process_events_line(ass_track_t* track, char *str) +static void event_format_fallback(ASS_Track *track) { - if (!strncmp(str, "Format:", 7)) { - char* p = str + 7; - skip_spaces(&p); - track->event_format = strdup(p); - mp_msg(MSGT_ASS, MSGL_DBG2, "Event format: %s\n", track->event_format); - } else if (!strncmp(str, "Dialogue:", 9)) { - // This should never be reached for embedded subtitles. - // They have slightly different format and are parsed in ass_process_chunk, - // called directly from demuxer - int eid; - ass_event_t* event; - - str += 9; - skip_spaces(&str); - - eid = ass_alloc_event(track); - event = track->events + eid; - - process_event_tail(track, event, str, 0); - } else { - mp_msg(MSGT_ASS, MSGL_V, "Not understood: %s \n", str); - } - return 0; + track->parser_priv->state = PST_EVENTS; + if (track->track_type == TRACK_TYPE_SSA) + track->event_format = strdup("Format: Marked, Start, End, Style, " + "Name, MarginL, MarginR, MarginV, Effect, Text"); + else + track->event_format = strdup("Format: Layer, Start, End, Style, " + "Actor, MarginL, MarginR, MarginV, Effect, Text"); + ass_msg(track->library, MSGL_V, + "No event format found, using fallback"); +} + +static int process_events_line(ASS_Track *track, char *str) +{ + if (!strncmp(str, "Format:", 7)) { + char *p = str + 7; + skip_spaces(&p); + track->event_format = strdup(p); + ass_msg(track->library, MSGL_DBG2, "Event format: %s", track->event_format); + } else if (!strncmp(str, "Dialogue:", 9)) { + // This should never be reached for embedded subtitles. + // They have slightly different format and are parsed in ass_process_chunk, + // called directly from demuxer + int eid; + ASS_Event *event; + + str += 9; + skip_spaces(&str); + + eid = ass_alloc_event(track); + event = track->events + eid; + + // We can't parse events with event_format + if (!track->event_format) + event_format_fallback(track); + + process_event_tail(track, event, str, 0); + } else { + ass_msg(track->library, MSGL_V, "Not understood: '%s'", str); + } + return 0; } // Copied from mkvtoolnix -static unsigned char* decode_chars(unsigned char c1, unsigned char c2, - unsigned char c3, unsigned char c4, unsigned char* dst, int cnt) +static unsigned char *decode_chars(unsigned char c1, unsigned char c2, + unsigned char c3, unsigned char c4, + unsigned char *dst, int cnt) { - uint32_t value; - unsigned char bytes[3]; - int i; - - value = ((c1 - 33) << 18) + ((c2 - 33) << 12) + ((c3 - 33) << 6) + (c4 - 33); - bytes[2] = value & 0xff; - bytes[1] = (value & 0xff00) >> 8; - bytes[0] = (value & 0xff0000) >> 16; - - for (i = 0; i < cnt; ++i) - *dst++ = bytes[i]; - return dst; + uint32_t value; + unsigned char bytes[3]; + int i; + + value = + ((c1 - 33) << 18) + ((c2 - 33) << 12) + ((c3 - 33) << 6) + (c4 - + 33); + bytes[2] = value & 0xff; + bytes[1] = (value & 0xff00) >> 8; + bytes[0] = (value & 0xff0000) >> 16; + + for (i = 0; i < cnt; ++i) + *dst++ = bytes[i]; + return dst; } -static int decode_font(ass_track_t* track) +static int decode_font(ASS_Track *track) { - unsigned char* p; - unsigned char* q; - int i; - int size; // original size - int dsize; // decoded size - unsigned char* buf = 0; - - mp_msg(MSGT_ASS, MSGL_V, "font: %d bytes encoded data \n", track->parser_priv->fontdata_used); - size = track->parser_priv->fontdata_used; - if (size % 4 == 1) { - mp_msg(MSGT_ASS, MSGL_ERR, MSGTR_LIBASS_BadEncodedDataSize); - goto error_decode_font; - } - buf = malloc(size / 4 * 3 + 2); - q = buf; - for (i = 0, p = (unsigned char*)track->parser_priv->fontdata; i < size / 4; i++, p+=4) { - q = decode_chars(p[0], p[1], p[2], p[3], q, 3); - } - if (size % 4 == 2) { - q = decode_chars(p[0], p[1], 0, 0, q, 1); - } else if (size % 4 == 3) { - q = decode_chars(p[0], p[1], p[2], 0, q, 2); - } - dsize = q - buf; - assert(dsize <= size / 4 * 3 + 2); - - if (track->library->extract_fonts) { - ass_add_font(track->library, track->parser_priv->fontname, (char*)buf, dsize); - buf = 0; - } - -error_decode_font: - if (buf) free(buf); - free(track->parser_priv->fontname); - free(track->parser_priv->fontdata); - track->parser_priv->fontname = 0; - track->parser_priv->fontdata = 0; - track->parser_priv->fontdata_size = 0; - track->parser_priv->fontdata_used = 0; - return 0; + unsigned char *p; + unsigned char *q; + int i; + int size; // original size + int dsize; // decoded size + unsigned char *buf = 0; + + ass_msg(track->library, MSGL_V, "Font: %d bytes encoded data", + track->parser_priv->fontdata_used); + size = track->parser_priv->fontdata_used; + if (size % 4 == 1) { + ass_msg(track->library, MSGL_ERR, "Bad encoded data size"); + goto error_decode_font; + } + buf = malloc(size / 4 * 3 + 2); + q = buf; + for (i = 0, p = (unsigned char *) track->parser_priv->fontdata; + i < size / 4; i++, p += 4) { + q = decode_chars(p[0], p[1], p[2], p[3], q, 3); + } + if (size % 4 == 2) { + q = decode_chars(p[0], p[1], 0, 0, q, 1); + } else if (size % 4 == 3) { + q = decode_chars(p[0], p[1], p[2], 0, q, 2); + } + dsize = q - buf; + assert(dsize <= size / 4 * 3 + 2); + + if (track->library->extract_fonts) { + ass_add_font(track->library, track->parser_priv->fontname, + (char *) buf, dsize); + buf = 0; + } + + error_decode_font: + if (buf) + free(buf); + free(track->parser_priv->fontname); + free(track->parser_priv->fontdata); + track->parser_priv->fontname = 0; + track->parser_priv->fontdata = 0; + track->parser_priv->fontdata_size = 0; + track->parser_priv->fontdata_used = 0; + return 0; } -static int process_fonts_line(ass_track_t* track, char *str) +static int process_fonts_line(ASS_Track *track, char *str) { - int len; - - if (!strncmp(str, "fontname:", 9)) { - char* p = str + 9; - skip_spaces(&p); - if (track->parser_priv->fontname) { - decode_font(track); - } - track->parser_priv->fontname = strdup(p); - mp_msg(MSGT_ASS, MSGL_V, "fontname: %s\n", track->parser_priv->fontname); - return 0; - } - - if (!track->parser_priv->fontname) { - mp_msg(MSGT_ASS, MSGL_V, "Not understood: %s \n", str); - return 0; - } - - len = strlen(str); - if (len > 80) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FontLineTooLong, len, str); - return 0; - } - if (track->parser_priv->fontdata_used + len > track->parser_priv->fontdata_size) { - track->parser_priv->fontdata_size += 100 * 1024; - track->parser_priv->fontdata = realloc(track->parser_priv->fontdata, track->parser_priv->fontdata_size); - } - memcpy(track->parser_priv->fontdata + track->parser_priv->fontdata_used, str, len); - track->parser_priv->fontdata_used += len; - - return 0; + int len; + + if (!strncmp(str, "fontname:", 9)) { + char *p = str + 9; + skip_spaces(&p); + if (track->parser_priv->fontname) { + decode_font(track); + } + track->parser_priv->fontname = strdup(p); + ass_msg(track->library, MSGL_V, "Fontname: %s", + track->parser_priv->fontname); + return 0; + } + + if (!track->parser_priv->fontname) { + ass_msg(track->library, MSGL_V, "Not understood: '%s'", str); + return 0; + } + + len = strlen(str); + if (len > 80) { + ass_msg(track->library, MSGL_WARN, "Font line too long: %d, %s", + len, str); + return 0; + } + if (track->parser_priv->fontdata_used + len > + track->parser_priv->fontdata_size) { + track->parser_priv->fontdata_size += 100 * 1024; + track->parser_priv->fontdata = + realloc(track->parser_priv->fontdata, + track->parser_priv->fontdata_size); + } + memcpy(track->parser_priv->fontdata + track->parser_priv->fontdata_used, + str, len); + track->parser_priv->fontdata_used += len; + + return 0; } /** @@ -664,67 +738,72 @@ static int process_fonts_line(ass_track_t* track, char *str) * \param track track * \param str string to parse, zero-terminated */ -static int process_line(ass_track_t* track, char *str) +static int process_line(ASS_Track *track, char *str) { - if (!strncasecmp(str, "[Script Info]", 13)) { - track->parser_priv->state = PST_INFO; - } else if (!strncasecmp(str, "[V4 Styles]", 11)) { - track->parser_priv->state = PST_STYLES; - track->track_type = TRACK_TYPE_SSA; - } else if (!strncasecmp(str, "[V4+ Styles]", 12)) { - track->parser_priv->state = PST_STYLES; - track->track_type = TRACK_TYPE_ASS; - } else if (!strncasecmp(str, "[Events]", 8)) { - track->parser_priv->state = PST_EVENTS; - } else if (!strncasecmp(str, "[Fonts]", 7)) { - track->parser_priv->state = PST_FONTS; - } else { - switch (track->parser_priv->state) { - case PST_INFO: - process_info_line(track, str); - break; - case PST_STYLES: - process_styles_line(track, str); - break; - case PST_EVENTS: - process_events_line(track, str); - break; - case PST_FONTS: - process_fonts_line(track, str); - break; - default: - break; - } - } - - // there is no explicit end-of-font marker in ssa/ass - if ((track->parser_priv->state != PST_FONTS) && (track->parser_priv->fontname)) - decode_font(track); - - return 0; + if (!strncasecmp(str, "[Script Info]", 13)) { + track->parser_priv->state = PST_INFO; + } else if (!strncasecmp(str, "[V4 Styles]", 11)) { + track->parser_priv->state = PST_STYLES; + track->track_type = TRACK_TYPE_SSA; + } else if (!strncasecmp(str, "[V4+ Styles]", 12)) { + track->parser_priv->state = PST_STYLES; + track->track_type = TRACK_TYPE_ASS; + } else if (!strncasecmp(str, "[Events]", 8)) { + track->parser_priv->state = PST_EVENTS; + } else if (!strncasecmp(str, "[Fonts]", 7)) { + track->parser_priv->state = PST_FONTS; + } else { + switch (track->parser_priv->state) { + case PST_INFO: + process_info_line(track, str); + break; + case PST_STYLES: + process_styles_line(track, str); + break; + case PST_EVENTS: + process_events_line(track, str); + break; + case PST_FONTS: + process_fonts_line(track, str); + break; + default: + break; + } + } + + // there is no explicit end-of-font marker in ssa/ass + if ((track->parser_priv->state != PST_FONTS) + && (track->parser_priv->fontname)) + decode_font(track); + + return 0; } -static int process_text(ass_track_t* track, char* str) +static int process_text(ASS_Track *track, char *str) { - char* p = str; - while(1) { - char* q; - while (1) { - if ((*p=='\r')||(*p=='\n')) ++p; - else if (p[0]=='\xef' && p[1]=='\xbb' && p[2]=='\xbf') p+=3; // U+FFFE (BOM) - else break; - } - for (q=p; ((*q!='\0')&&(*q!='\r')&&(*q!='\n')); ++q) {}; - if (q==p) - break; - if (*q != '\0') - *(q++) = '\0'; - process_line(track, p); - if (*q == '\0') - break; - p = q; - } - return 0; + char *p = str; + while (1) { + char *q; + while (1) { + if ((*p == '\r') || (*p == '\n')) + ++p; + else if (p[0] == '\xef' && p[1] == '\xbb' && p[2] == '\xbf') + p += 3; // U+FFFE (BOM) + else + break; + } + for (q = p; ((*q != '\0') && (*q != '\r') && (*q != '\n')); ++q) { + }; + if (q == p) + break; + if (*q != '\0') + *(q++) = '\0'; + process_line(track, p); + if (*q == '\0') + break; + p = q; + } + return 0; } /** @@ -733,16 +812,16 @@ static int process_text(ass_track_t* track, char* str) * \param data string to parse * \param size length of data */ -void ass_process_data(ass_track_t* track, char* data, int size) +void ass_process_data(ASS_Track *track, char *data, int size) { - char* str = malloc(size + 1); + char *str = malloc(size + 1); - memcpy(str, data, size); - str[size] = '\0'; + memcpy(str, data, size); + str[size] = '\0'; - mp_msg(MSGT_ASS, MSGL_V, "event: %s\n", str); - process_text(track, str); - free(str); + ass_msg(track->library, MSGL_V, "Event: %s", str); + process_text(track, str); + free(str); } /** @@ -752,30 +831,25 @@ void ass_process_data(ass_track_t* track, char* data, int size) * \param size length of data CodecPrivate section contains [Stream Info] and [V4+ Styles] ([V4 Styles] for SSA) sections */ -void ass_process_codec_private(ass_track_t* track, char *data, int size) +void ass_process_codec_private(ASS_Track *track, char *data, int size) { - ass_process_data(track, data, size); - - if (!track->event_format) { - // probably an mkv produced by ancient mkvtoolnix - // such files don't have [Events] and Format: headers - track->parser_priv->state = PST_EVENTS; - if (track->track_type == TRACK_TYPE_SSA) - track->event_format = strdup("Format: Marked, Start, End, Style, Name, MarginL, MarginR, MarginV, Effect, Text"); - else - track->event_format = strdup("Format: Layer, Start, End, Style, Actor, MarginL, MarginR, MarginV, Effect, Text"); - } - - process_force_style(track); + ass_process_data(track, data, size); + + // probably an mkv produced by ancient mkvtoolnix + // such files don't have [Events] and Format: headers + if (!track->event_format) + event_format_fallback(track); + + ass_process_force_style(track); } -static int check_duplicate_event(ass_track_t* track, int ReadOrder) +static int check_duplicate_event(ASS_Track *track, int ReadOrder) { - int i; - for (i = 0; in_events - 1; ++i) // ignoring last event, it is the one we are comparing with - if (track->events[i].ReadOrder == ReadOrder) - return 1; - return 0; + int i; + for (i = 0; i < track->n_events - 1; ++i) // ignoring last event, it is the one we are comparing with + if (track->events[i].ReadOrder == ReadOrder) + return 1; + return 0; } /** @@ -786,51 +860,53 @@ static int check_duplicate_event(ass_track_t* track, int ReadOrder) * \param timecode starting time of the event (milliseconds) * \param duration duration of the event (milliseconds) */ -void ass_process_chunk(ass_track_t* track, char *data, int size, long long timecode, long long duration) +void ass_process_chunk(ASS_Track *track, char *data, int size, + long long timecode, long long duration) { - char* str; - int eid; - char* p; - char* token; - ass_event_t* event; - - if (!track->event_format) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_EventFormatHeaderMissing); - return; - } - - str = malloc(size + 1); - memcpy(str, data, size); - str[size] = '\0'; - mp_msg(MSGT_ASS, MSGL_V, "event at %" PRId64 ", +%" PRId64 ": %s \n", (int64_t)timecode, (int64_t)duration, str); - - eid = ass_alloc_event(track); - event = track->events + eid; - - p = str; - - do { - NEXT(p, token); - event->ReadOrder = atoi(token); - if (check_duplicate_event(track, event->ReadOrder)) - break; - - NEXT(p, token); - event->Layer = atoi(token); - - process_event_tail(track, event, p, 3); - - event->Start = timecode; - event->Duration = duration; - - free(str); - return; -// dump_events(tid); - } while (0); - // some error - ass_free_event(track, eid); - track->n_events--; - free(str); + char *str; + int eid; + char *p; + char *token; + ASS_Event *event; + + if (!track->event_format) { + ass_msg(track->library, MSGL_WARN, "Event format header missing"); + return; + } + + str = malloc(size + 1); + memcpy(str, data, size); + str[size] = '\0'; + ass_msg(track->library, MSGL_V, "Event at %" PRId64 ", +%" PRId64 ": %s", + (int64_t) timecode, (int64_t) duration, str); + + eid = ass_alloc_event(track); + event = track->events + eid; + + p = str; + + do { + NEXT(p, token); + event->ReadOrder = atoi(token); + if (check_duplicate_event(track, event->ReadOrder)) + break; + + NEXT(p, token); + event->Layer = atoi(token); + + process_event_tail(track, event, p, 3); + + event->Start = timecode; + event->Duration = duration; + + free(str); + return; +// dump_events(tid); + } while (0); + // some error + ass_free_event(track, eid); + track->n_events--; + free(str); } #ifdef CONFIG_ICONV @@ -840,75 +916,78 @@ void ass_process_chunk(ass_track_t* track, char *data, int size, long long timec * \param size buffer size * \return a pointer to recoded buffer, caller is responsible for freeing it **/ -static char* sub_recode(char* data, size_t size, char* codepage) +static char *sub_recode(ASS_Library *library, char *data, size_t size, + char *codepage) { - static iconv_t icdsc = (iconv_t)(-1); - char* tocp = "UTF-8"; - char* outbuf; - assert(codepage); + iconv_t icdsc; + char *tocp = "UTF-8"; + char *outbuf; + assert(codepage); - { - const char* cp_tmp = codepage; + { + const char *cp_tmp = codepage; #ifdef CONFIG_ENCA - char enca_lang[3], enca_fallback[100]; - if (sscanf(codepage, "enca:%2s:%99s", enca_lang, enca_fallback) == 2 - || sscanf(codepage, "ENCA:%2s:%99s", enca_lang, enca_fallback) == 2) { - cp_tmp = guess_buffer_cp((unsigned char*)data, size, enca_lang, enca_fallback); - } + char enca_lang[3], enca_fallback[100]; + if (sscanf(codepage, "enca:%2s:%99s", enca_lang, enca_fallback) == 2 + || sscanf(codepage, "ENCA:%2s:%99s", enca_lang, + enca_fallback) == 2) { + cp_tmp = + ass_guess_buffer_cp(library, (unsigned char *) data, size, + enca_lang, enca_fallback); + } #endif - if ((icdsc = iconv_open (tocp, cp_tmp)) != (iconv_t)(-1)){ - mp_msg(MSGT_ASS,MSGL_V,"LIBSUB: opened iconv descriptor.\n"); - } else - mp_msg(MSGT_ASS,MSGL_ERR,MSGTR_LIBASS_ErrorOpeningIconvDescriptor); - } - - { - size_t osize = size; - size_t ileft = size; - size_t oleft = size - 1; - char* ip; - char* op; - size_t rc; - int clear = 0; - - outbuf = malloc(osize); - ip = data; - op = outbuf; - - while (1) { - if (ileft) - rc = iconv(icdsc, &ip, &ileft, &op, &oleft); - else {// clear the conversion state and leave - clear = 1; - rc = iconv(icdsc, NULL, NULL, &op, &oleft); - } - if (rc == (size_t)(-1)) { - if (errno == E2BIG) { - size_t offset = op - outbuf; - outbuf = (char*)realloc(outbuf, osize + size); - op = outbuf + offset; - osize += size; - oleft += size; - } else { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorRecodingFile); - return NULL; - } - } else - if (clear) - break; - } - outbuf[osize - oleft - 1] = 0; - } - - if (icdsc != (iconv_t)(-1)) { - (void)iconv_close(icdsc); - icdsc = (iconv_t)(-1); - mp_msg(MSGT_ASS,MSGL_V,"LIBSUB: closed iconv descriptor.\n"); - } - - return outbuf; + if ((icdsc = iconv_open(tocp, cp_tmp)) != (iconv_t) (-1)) { + ass_msg(library, MSGL_V, "Opened iconv descriptor"); + } else + ass_msg(library, MSGL_ERR, "Error opening iconv descriptor"); + } + + { + size_t osize = size; + size_t ileft = size; + size_t oleft = size - 1; + char *ip; + char *op; + size_t rc; + int clear = 0; + + outbuf = malloc(osize); + ip = data; + op = outbuf; + + while (1) { + if (ileft) + rc = iconv(icdsc, &ip, &ileft, &op, &oleft); + else { // clear the conversion state and leave + clear = 1; + rc = iconv(icdsc, NULL, NULL, &op, &oleft); + } + if (rc == (size_t) (-1)) { + if (errno == E2BIG) { + size_t offset = op - outbuf; + outbuf = (char *) realloc(outbuf, osize + size); + op = outbuf + offset; + osize += size; + oleft += size; + } else { + ass_msg(library, MSGL_WARN, "Error recoding file"); + return NULL; + } + } else if (clear) + break; + } + outbuf[osize - oleft - 1] = 0; + } + + if (icdsc != (iconv_t) (-1)) { + (void) iconv_close(icdsc); + icdsc = (iconv_t) (-1); + ass_msg(library, MSGL_V, "Closed iconv descriptor"); + } + + return outbuf; } -#endif // ICONV +#endif // ICONV /** * \brief read file contents into newly allocated buffer @@ -916,86 +995,91 @@ static char* sub_recode(char* data, size_t size, char* codepage) * \param bufsize out: file size * \return pointer to file contents. Caller is responsible for its deallocation. */ -static char* read_file(char* fname, size_t *bufsize) +static char *read_file(ASS_Library *library, char *fname, size_t *bufsize) { - int res; - long sz; - long bytes_read; - char* buf; - - FILE* fp = fopen(fname, "rb"); - if (!fp) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FopenFailed, fname); - return 0; - } - res = fseek(fp, 0, SEEK_END); - if (res == -1) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FseekFailed, fname); - fclose(fp); - return 0; - } - - sz = ftell(fp); - rewind(fp); - - if (sz > 10*1024*1024) { - mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_RefusingToLoadSubtitlesLargerThan10M, fname); - fclose(fp); - return 0; - } - - mp_msg(MSGT_ASS, MSGL_V, "file size: %ld\n", sz); - - buf = malloc(sz + 1); - assert(buf); - bytes_read = 0; - do { - res = fread(buf + bytes_read, 1, sz - bytes_read, fp); - if (res <= 0) { - mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_ReadFailed, errno, strerror(errno)); - fclose(fp); - free(buf); - return 0; - } - bytes_read += res; - } while (sz - bytes_read > 0); - buf[sz] = '\0'; - fclose(fp); - - if (bufsize) - *bufsize = sz; - return buf; + int res; + long sz; + long bytes_read; + char *buf; + + FILE *fp = fopen(fname, "rb"); + if (!fp) { + ass_msg(library, MSGL_WARN, + "ass_read_file(%s): fopen failed", fname); + return 0; + } + res = fseek(fp, 0, SEEK_END); + if (res == -1) { + ass_msg(library, MSGL_WARN, + "ass_read_file(%s): fseek failed", fname); + fclose(fp); + return 0; + } + + sz = ftell(fp); + rewind(fp); + + if (sz > 10 * 1024 * 1024) { + ass_msg(library, MSGL_INFO, + "ass_read_file(%s): Refusing to load subtitles " + "larger than 10MiB", fname); + fclose(fp); + return 0; + } + + ass_msg(library, MSGL_V, "File size: %ld", sz); + + buf = malloc(sz + 1); + assert(buf); + bytes_read = 0; + do { + res = fread(buf + bytes_read, 1, sz - bytes_read, fp); + if (res <= 0) { + ass_msg(library, MSGL_INFO, "Read failed, %d: %s", errno, + strerror(errno)); + fclose(fp); + free(buf); + return 0; + } + bytes_read += res; + } while (sz - bytes_read > 0); + buf[sz] = '\0'; + fclose(fp); + + if (bufsize) + *bufsize = sz; + return buf; } /* * \param buf pointer to subtitle text in utf-8 */ -static ass_track_t* parse_memory(ass_library_t* library, char* buf) +static ASS_Track *parse_memory(ASS_Library *library, char *buf) { - ass_track_t* track; - int i; + ASS_Track *track; + int i; - track = ass_new_track(library); + track = ass_new_track(library); - // process header - process_text(track, buf); + // process header + process_text(track, buf); - // external SSA/ASS subs does not have ReadOrder field - for (i = 0; i < track->n_events; ++i) - track->events[i].ReadOrder = i; + // external SSA/ASS subs does not have ReadOrder field + for (i = 0; i < track->n_events; ++i) + track->events[i].ReadOrder = i; - // there is no explicit end-of-font marker in ssa/ass - if (track->parser_priv->fontname) - decode_font(track); + // there is no explicit end-of-font marker in ssa/ass + if (track->parser_priv->fontname) + decode_font(track); - if (track->track_type == TRACK_TYPE_UNKNOWN) { - ass_free_track(track); - return 0; - } + if (track->track_type == TRACK_TYPE_UNKNOWN) { + ass_free_track(track); + return 0; + } - process_force_style(track); + ass_process_force_style(track); - return track; + return track; } /** @@ -1006,51 +1090,56 @@ static ass_track_t* parse_memory(ass_library_t* library, char* buf) * \param codepage recode buffer contents from given codepage * \return newly allocated track */ -ass_track_t* ass_read_memory(ass_library_t* library, char* buf, size_t bufsize, char* codepage) +ASS_Track *ass_read_memory(ASS_Library *library, char *buf, + size_t bufsize, char *codepage) { - ass_track_t* track; - int need_free = 0; + ASS_Track *track; + int need_free = 0; - if (!buf) - return 0; + if (!buf) + return 0; #ifdef CONFIG_ICONV - if (codepage) - buf = sub_recode(buf, bufsize, codepage); - if (!buf) - return 0; - else - need_free = 1; + if (codepage) { + buf = sub_recode(library, buf, bufsize, codepage); + if (!buf) + return 0; + else + need_free = 1; + } #endif - track = parse_memory(library, buf); - if (need_free) - free(buf); - if (!track) - return 0; - - mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_AddedSubtitleFileMemory, track->n_styles, track->n_events); - return track; + track = parse_memory(library, buf); + if (need_free) + free(buf); + if (!track) + return 0; + + ass_msg(library, MSGL_INFO, "Added subtitle file: " + " (%d styles, %d events)", + track->n_styles, track->n_events); + return track; } -char* read_file_recode(char* fname, char* codepage, size_t* size) +static char *read_file_recode(ASS_Library *library, char *fname, + char *codepage, size_t *size) { - char* buf; - size_t bufsize; + char *buf; + size_t bufsize; - buf = read_file(fname, &bufsize); - if (!buf) - return 0; + buf = read_file(library, fname, &bufsize); + if (!buf) + return 0; #ifdef CONFIG_ICONV - if (codepage) { - char* tmpbuf = sub_recode(buf, bufsize, codepage); - free(buf); - buf = tmpbuf; - } - if (!buf) - return 0; + if (codepage) { + char *tmpbuf = sub_recode(library, buf, bufsize, codepage); + free(buf); + buf = tmpbuf; + } + if (!buf) + return 0; #endif - *size = bufsize; - return buf; + *size = bufsize; + return buf; } /** @@ -1060,83 +1149,98 @@ char* read_file_recode(char* fname, char* codepage, size_t* size) * \param codepage recode buffer contents from given codepage * \return newly allocated track */ -ass_track_t* ass_read_file(ass_library_t* library, char* fname, char* codepage) +ASS_Track *ass_read_file(ASS_Library *library, char *fname, + char *codepage) { - char* buf; - ass_track_t* track; - size_t bufsize; + char *buf; + ASS_Track *track; + size_t bufsize; - buf = read_file_recode(fname, codepage, &bufsize); - if (!buf) - return 0; - track = parse_memory(library, buf); - free(buf); - if (!track) - return 0; + buf = read_file_recode(library, fname, codepage, &bufsize); + if (!buf) + return 0; + track = parse_memory(library, buf); + free(buf); + if (!track) + return 0; - track->name = strdup(fname); + track->name = strdup(fname); - mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_AddedSubtitleFileFname, fname, track->n_styles, track->n_events); + ass_msg(library, MSGL_INFO, + "Added subtitle file: '%s' (%d styles, %d events)", + fname, track->n_styles, track->n_events); -// dump_events(forced_tid); - return track; + return track; } /** * \brief read styles from file into already initialized track */ -int ass_read_styles(ass_track_t* track, char* fname, char* codepage) +int ass_read_styles(ASS_Track *track, char *fname, char *codepage) { - char* buf; - parser_state_t old_state; - size_t sz; + char *buf; + ParserState old_state; + size_t sz; - buf = read_file(fname, &sz); - if (!buf) - return 1; + buf = read_file(track->library, fname, &sz); + if (!buf) + return 1; #ifdef CONFIG_ICONV - if (codepage) { - char* tmpbuf; - tmpbuf = sub_recode(buf, sz, codepage); - free(buf); - buf = tmpbuf; - } - if (!buf) - return 0; + if (codepage) { + char *tmpbuf; + tmpbuf = sub_recode(track->library, buf, sz, codepage); + free(buf); + buf = tmpbuf; + } + if (!buf) + return 0; #endif - old_state = track->parser_priv->state; - track->parser_priv->state = PST_STYLES; - process_text(track, buf); - track->parser_priv->state = old_state; + old_state = track->parser_priv->state; + track->parser_priv->state = PST_STYLES; + process_text(track, buf); + track->parser_priv->state = old_state; - return 0; + return 0; } -long long ass_step_sub(ass_track_t* track, long long now, int movement) { - int i; - - if (movement == 0) return 0; - if (track->n_events == 0) return 0; - - if (movement < 0) - for (i = 0; (i < track->n_events) && ((long long)(track->events[i].Start + track->events[i].Duration) <= now); ++i) {} - else - for (i = track->n_events - 1; (i >= 0) && ((long long)(track->events[i].Start) > now); --i) {} - - // -1 and n_events are ok - assert(i >= -1); assert(i <= track->n_events); - i += movement; - if (i < 0) i = 0; - if (i >= track->n_events) i = track->n_events - 1; - return ((long long)track->events[i].Start) - now; +long long ass_step_sub(ASS_Track *track, long long now, int movement) +{ + int i; + + if (movement == 0) + return 0; + if (track->n_events == 0) + return 0; + + if (movement < 0) + for (i = 0; + (i < track->n_events) + && + ((long long) (track->events[i].Start + + track->events[i].Duration) <= now); ++i) { + } else + for (i = track->n_events - 1; + (i >= 0) && ((long long) (track->events[i].Start) > now); + --i) { + } + + // -1 and n_events are ok + assert(i >= -1); + assert(i <= track->n_events); + i += movement; + if (i < 0) + i = 0; + if (i >= track->n_events) + i = track->n_events - 1; + return ((long long) track->events[i].Start) - now; } -ass_track_t* ass_new_track(ass_library_t* library) { - ass_track_t* track = calloc(1, sizeof(ass_track_t)); - track->library = library; - track->ScaledBorderAndShadow = 1; - track->parser_priv = calloc(1, sizeof(parser_priv_t)); - return track; +ASS_Track *ass_new_track(ASS_Library *library) +{ + ASS_Track *track = calloc(1, sizeof(ASS_Track)); + track->library = library; + track->ScaledBorderAndShadow = 1; + track->parser_priv = calloc(1, sizeof(ASS_ParserPriv)); + return track; } - diff --git a/libass/ass.h b/libass/ass.h index 12f16fef5d..e7674a736c 100644 --- a/libass/ass.h +++ b/libass/ass.h @@ -1,5 +1,3 @@ -// -*- c-basic-offset: 8; indent-tabs-mode: t -*- -// vim:ts=8:sw=8:noet:ai: /* * Copyright (C) 2006 Evgeniy Stepanov * @@ -24,136 +22,271 @@ #define LIBASS_ASS_H #include +#include #include "ass_types.h" -/// Libass renderer object. Contents are private. -typedef struct ass_renderer_s ass_renderer_t; +#define LIBASS_VERSION 0x00908000 -/// a linked list of images produced by ass renderer -typedef struct ass_image_s { - int w, h; // bitmap width/height - int stride; // bitmap stride - unsigned char* bitmap; // 1bpp stride*h alpha buffer - // Actual bitmap size may be as low as - // stride * (h-1) + w - uint32_t color; // RGBA - int dst_x, dst_y; // bitmap placement inside the video frame - - struct ass_image_s* next; // linked list -} ass_image_t; +/* + * A linked list of images produced by an ass renderer. + * + * These images have to be rendered in-order for the correct screen + * composition. The libass renderer clips these bitmaps to the frame size. + * w/h can be zero, in this case the bitmap should not be rendered at all. + * The last bitmap row is not guaranteed to be padded up to stride size, + * e.g. in the worst case a bitmap has the size stride * (h - 1) + w. + */ +typedef struct ass_image { + int w, h; // Bitmap width/height + int stride; // Bitmap stride + unsigned char *bitmap; // 1bpp stride*h alpha buffer + // Note: the last row may not be padded to + // bitmap stride! + uint32_t color; // Bitmap color and alpha, RGBA + int dst_x, dst_y; // Bitmap placement inside the video frame + + struct ass_image *next; // Next image, or NULL +} ASS_Image; -/// Hinting type -typedef enum {ASS_HINTING_NONE = 0, - ASS_HINTING_LIGHT, - ASS_HINTING_NORMAL, - ASS_HINTING_NATIVE -} ass_hinting_t; +/* + * Hinting type. (see ass_set_hinting below) + * + * FreeType's native hinter is still buggy sometimes and it is recommended + * to use the light autohinter, ASS_HINTING_LIGHT, instead. For best + * compatibility with problematic fonts, disable hinting. + */ +typedef enum { + ASS_HINTING_NONE = 0, + ASS_HINTING_LIGHT, + ASS_HINTING_NORMAL, + ASS_HINTING_NATIVE +} ASS_Hinting; /** - * \brief initialize the library + * \brief Initialize the library. * \return library handle or NULL if failed */ -ass_library_t* ass_library_init(void); +ASS_Library *ass_library_init(void); /** - * \brief finalize the library + * \brief Finalize the library * \param priv library handle */ -void ass_library_done(ass_library_t*); +void ass_library_done(ASS_Library *priv); /** - * \brief set private font directory + * \brief Set private font directory. * It is used for saving embedded fonts and also