diff --git a/aegisub/libass/Makefile.am b/aegisub/libass/Makefile.am index 98cda6ee8..45590435b 100644 --- a/aegisub/libass/Makefile.am +++ b/aegisub/libass/Makefile.am @@ -10,24 +10,16 @@ libass_aegisub_a_SOURCES = \ ass.c \ ass_bitmap.c \ ass_cache.c \ + ass_drawing.c \ ass_font.c \ ass_fontconfig.c \ ass_library.c \ ass_render.c \ ass_utils.c \ - mputils.c \ - ass.h \ - ass_bitmap.h \ - ass_cache.h \ - ass_font.h \ - ass_fontconfig.h \ - ass_library.h \ - ass_types.h \ - ass_utils.h \ - help_mp.h \ - libass_msvc.patch \ - mputils.h \ - msvc.h + mputils.c + +libass_aegisub_a_SOURCES += \ + *.h EXTRA_DIST= \ ass_cache_template.c diff --git a/aegisub/libass/ass.c b/aegisub/libass/ass.c index b0c38c2ce..5aef14479 100644 --- a/aegisub/libass/ass.c +++ b/aegisub/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,130 @@ #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; +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; + parser_state_t 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_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); + } } /// \brief Allocate a new style struct /// \param track track /// \return style id -int ass_alloc_style(ass_track_t* track) { - int sid; - - assert(track->n_styles <= track->max_styles); +int ass_alloc_style(ass_track_t *track) +{ + int sid; - 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); - } - - sid = track->n_styles++; - memset(track->styles + sid, 0, sizeof(ass_style_t)); - return sid; + 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); + } + + sid = track->n_styles++; + memset(track->styles + sid, 0, sizeof(ass_style_t)); + return sid; } /// \brief Allocate a new event struct /// \param track track /// \return event id -int ass_alloc_event(ass_track_t* track) { - int eid; - - assert(track->n_events <= track->max_events); +int ass_alloc_event(ass_track_t *track) +{ + int eid; - 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); - } - - eid = track->n_events++; - memset(track->events + eid, 0, sizeof(ass_event_t)); - return eid; + 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); + } + + eid = track->n_events++; + memset(track->events + eid, 0, sizeof(ass_event_t)); + 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_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_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_t *track, int sid) +{ + ass_style_t *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 +171,54 @@ 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_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; + ass_msg(MSGL_WARN, MSGTR_LIBASS_NoStyleNamedXFoundUsingY, + 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(char *p) +{ + uint32_t tmp; + (void) strtocolor(&p, &tmp); + 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(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(MSGL_WARN, MSGTR_LIBASS_BadTimestamp); + 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,14 +228,14 @@ 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(MSGL_DBG2, "%s = %s\n", #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(MSGL_DBG2, "%s = %s\n", #name, token); + #define COLORVAL(name) ANYVAL(name,string2color) #define INTVAL(name) ANYVAL(name,atoi) #define FPVAL(name) ANYVAL(name,atof) @@ -225,98 +243,102 @@ static int numpad2align(int val) { #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(MSGL_DBG2, "%s = %s\n", #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 * \param event parsed data goes here * \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_t *track, ass_event_t *event, + char *str, int n_ignored) { - char* token; - char* tname; - char* p = str; - int i; - ass_event_t* target = event; + char *token; + char *tname; + char *p = str; + int i; + ass_event_t *target = event; - char* format = strdup(track->event_format); - char* q = format; // format scanning pointer + 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"); - } + 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); - } + 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); + 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(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; + 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; } /** @@ -324,73 +346,77 @@ 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; +void ass_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(!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); + if (!list) + return; - 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 = '.'; - } + 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 = '.'; + } } /** @@ -398,326 +424,368 @@ void process_force_style(ass_track_t* track) { * \param track 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_t *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; + 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"); - } + 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); + q = format = strdup(track->style_format); - style = track->styles + sid; - target = style; + ass_msg(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; - + 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; + } -static int process_styles_line(ass_track_t* track, char *str) +static int process_styles_line(ass_track_t *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(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; } -static int process_info_line(ass_track_t* track, char *str) +static int process_info_line(ass_track_t *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); + } + return 0; } -static int process_events_line(ass_track_t* track, char *str) +static void event_format_fallback(ass_track_t *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); + 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(MSGL_V, "No event format found, using fallback.\n"); +} - eid = ass_alloc_event(track); - event = track->events + eid; +static int process_events_line(ass_track_t *track, char *str) +{ + if (!strncmp(str, "Format:", 7)) { + char *p = str + 7; + skip_spaces(&p); + track->event_format = strdup(p); + ass_msg(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; - process_event_tail(track, event, str, 0); - } else { - mp_msg(MSGT_ASS, MSGL_V, "Not understood: %s \n", str); - } - return 0; + 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(MSGL_V, "Not understood: %s \n", 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; + 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; + 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; + 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_t *track) { - unsigned char* p; - unsigned char* q; - int i; - int size; // original size - int dsize; // decoded size - unsigned char* buf = 0; + 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; - } + ass_msg(MSGL_V, "font: %d bytes encoded data \n", + track->parser_priv->fontdata_used); + size = track->parser_priv->fontdata_used; + if (size % 4 == 1) { + ass_msg(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); -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; + 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_t *track, char *str) { - int len; + 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; - } + 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(MSGL_V, "fontname: %s\n", + track->parser_priv->fontname); + 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; + if (!track->parser_priv->fontname) { + ass_msg(MSGL_V, "Not understood: %s \n", str); + return 0; + } + + len = strlen(str); + if (len > 80) { + ass_msg(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; } /** * \brief Parse a header line * \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_t *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; - } - } + 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); + // 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; + return 0; } -static int process_text(ass_track_t* track, char* str) +static int process_text(ass_track_t *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; } /** @@ -726,16 +794,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_t *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(MSGL_V, "event: %s\n", str); + process_text(track, str); + free(str); } /** @@ -745,30 +813,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_t *track, char *data, int size) { - ass_process_data(track, data, 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"); - } + // probably an mkv produced by ancient mkvtoolnix + // such files don't have [Events] and Format: headers + if (!track->event_format) + event_format_fallback(track); - process_force_style(track); + ass_process_force_style(track); } -static int check_duplicate_event(ass_track_t* track, int ReadOrder) +static int check_duplicate_event(ass_track_t *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; } /** @@ -778,52 +841,54 @@ static int check_duplicate_event(ass_track_t* track, int ReadOrder) * \param size length of data * \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_t *track, char *data, int size, + long long timecode, long long duration) { - char* str; - int eid; - char* p; - char* token; - ass_event_t* event; + 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); + if (!track->event_format) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_EventFormatHeaderMissing); + return; + } - eid = ass_alloc_event(track); - event = track->events + eid; + str = malloc(size + 1); + memcpy(str, data, size); + str[size] = '\0'; + ass_msg(MSGL_V, "event at %" PRId64 ", +%" PRId64 ": %s \n", + (int64_t) timecode, (int64_t) duration, str); - p = str; - - do { - NEXT(p, token); - event->ReadOrder = atoi(token); - if (check_duplicate_event(track, event->ReadOrder)) - break; + eid = ass_alloc_event(track); + event = track->events + eid; - NEXT(p, token); - event->Layer = atoi(token); + p = str; - process_event_tail(track, event, p, 3); + do { + NEXT(p, token); + event->ReadOrder = atoi(token); + if (check_duplicate_event(track, event->ReadOrder)) + break; - 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); + 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 @@ -833,75 +898,79 @@ 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(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((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); - } + if ((icdsc = iconv_open(tocp, cp_tmp)) != (iconv_t) (-1)) { + ass_msg(MSGL_V, "LIBSUB: opened iconv descriptor.\n"); + } else + ass_msg(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; - } + { + size_t osize = size; + size_t ileft = size; + size_t oleft = size - 1; + char *ip; + char *op; + size_t rc; + int clear = 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; + 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(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); + ass_msg(MSGL_V, "LIBSUB: closed iconv descriptor.\n"); + } + + return outbuf; } -#endif // ICONV +#endif // ICONV /** * \brief read file contents into newly allocated buffer @@ -909,86 +978,88 @@ 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(char *fname, size_t *bufsize) { - int res; - long sz; - long bytes_read; - char* buf; + 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); + FILE *fp = fopen(fname, "rb"); + if (!fp) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_FopenFailed, fname); + return 0; + } + res = fseek(fp, 0, SEEK_END); + if (res == -1) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_FseekFailed, fname); + fclose(fp); + return 0; + } - 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; + sz = ftell(fp); + rewind(fp); + + if (sz > 10 * 1024 * 1024) { + ass_msg(MSGL_INFO, + MSGTR_LIBASS_RefusingToLoadSubtitlesLargerThan10M, fname); + fclose(fp); + return 0; + } + + ass_msg(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) { + ass_msg(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; } /* * \param buf pointer to subtitle text in utf-8 */ -static ass_track_t* parse_memory(ass_library_t* library, char* buf) +static ass_track_t *parse_memory(ass_library_t *library, char *buf) { - ass_track_t* track; - int i; - - track = ass_new_track(library); - - // process header - process_text(track, buf); + ass_track_t *track; + int i; - // external SSA/ASS subs does not have ReadOrder field - for (i = 0; i < track->n_events; ++i) - track->events[i].ReadOrder = i; + track = ass_new_track(library); - // there is no explicit end-of-font marker in ssa/ass - if (track->parser_priv->fontname) - decode_font(track); + // process header + process_text(track, buf); - if (track->track_type == TRACK_TYPE_UNKNOWN) { - ass_free_track(track); - return 0; - } + // external SSA/ASS subs does not have ReadOrder field + for (i = 0; i < track->n_events; ++i) + track->events[i].ReadOrder = i; - process_force_style(track); + // there is no explicit end-of-font marker in ssa/ass + if (track->parser_priv->fontname) + decode_font(track); - return track; + if (track->track_type == TRACK_TYPE_UNKNOWN) { + ass_free_track(track); + return 0; + } + + ass_process_force_style(track); + + return track; } /** @@ -998,52 +1069,54 @@ static ass_track_t* parse_memory(ass_library_t* library, char* buf) * \param bufsize size of buffer * \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_t *ass_read_memory(ass_library_t *library, char *buf, + size_t bufsize, char *codepage) { - ass_track_t* track; - int need_free = 0; - - if (!buf) - return 0; - -#ifdef CONFIG_ICONV - if (codepage) - buf = sub_recode(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; + ass_track_t *track; + int need_free = 0; - mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_AddedSubtitleFileMemory, track->n_styles, track->n_events); - return track; + if (!buf) + return 0; + +#ifdef CONFIG_ICONV + if (codepage) + buf = sub_recode(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; + + ass_msg(MSGL_INFO, MSGTR_LIBASS_AddedSubtitleFileMemory, + track->n_styles, track->n_events); + return track; } -char* read_file_recode(char* fname, char* codepage, size_t* size) +static char *read_file_recode(char *fname, char *codepage, size_t *size) { - char* buf; - size_t bufsize; - - buf = read_file(fname, &bufsize); - if (!buf) - return 0; + char *buf; + size_t bufsize; + + buf = read_file(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(buf, bufsize, codepage); + free(buf); + buf = tmpbuf; + } + if (!buf) + return 0; #endif - *size = bufsize; - return buf; + *size = bufsize; + return buf; } /** @@ -1052,84 +1125,99 @@ char* read_file_recode(char* fname, char* codepage, size_t* size) * \param fname file name * \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_t *ass_read_file(ass_library_t *library, char *fname, + char *codepage) { - char* buf; - ass_track_t* track; - size_t bufsize; + char *buf; + ass_track_t *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; - - track->name = strdup(fname); + buf = read_file_recode(fname, codepage, &bufsize); + if (!buf) + return 0; + track = parse_memory(library, buf); + free(buf); + if (!track) + return 0; - mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_AddedSubtitleFileFname, fname, track->n_styles, track->n_events); - -// dump_events(forced_tid); - return track; + track->name = strdup(fname); + + ass_msg(MSGL_INFO, MSGTR_LIBASS_AddedSubtitleFileFname, fname, + track->n_styles, track->n_events); + +// dump_events(forced_tid); + 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_t *track, char *fname, char *codepage) { - char* buf; - parser_state_t old_state; - size_t sz; + char *buf; + parser_state_t old_state; + size_t sz; - buf = read_file(fname, &sz); - if (!buf) - return 1; + buf = read_file(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(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; +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; + 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_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; } - diff --git a/aegisub/libass/ass.h b/aegisub/libass/ass.h index 4bdea3a4a..d911e9911 100644 --- a/aegisub/libass/ass.h +++ b/aegisub/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 * @@ -31,74 +29,75 @@ typedef struct ass_renderer_s ass_renderer_t; /// 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 - uint32_t color; // RGBA - int dst_x, dst_y; // bitmap placement inside the video frame + int w, h; // bitmap width/height + int stride; // bitmap stride + unsigned char *bitmap; // 1bpp stride*h alpha buffer + uint32_t color; // RGBA + int dst_x, dst_y; // bitmap placement inside the video frame - struct ass_image_s* next; // linked list + struct ass_image_s *next; // linked list } ass_image_t; /// Hinting type -typedef enum {ASS_HINTING_NONE = 0, - ASS_HINTING_LIGHT, - ASS_HINTING_NORMAL, - ASS_HINTING_NATIVE +typedef enum { ASS_HINTING_NONE = 0, + ASS_HINTING_LIGHT, + ASS_HINTING_NORMAL, + ASS_HINTING_NATIVE } ass_hinting_t; /** * \brief initialize the library * \return library handle or NULL if failed */ -ass_library_t* ass_library_init(void); +ass_library_t *ass_library_init(void); /** * \brief finalize the library * \param priv library handle */ -void ass_library_done(ass_library_t*); +void ass_library_done(ass_library_t *); /** * \brief set private font directory * It is used for saving embedded fonts and also in font lookup. */ -void ass_set_fonts_dir(ass_library_t* priv, const char* fonts_dir); +void ass_set_fonts_dir(ass_library_t *priv, const char *fonts_dir); -void ass_set_extract_fonts(ass_library_t* priv, int extract); +void ass_set_extract_fonts(ass_library_t *priv, int extract); -void ass_set_style_overrides(ass_library_t* priv, char** list); +void ass_set_style_overrides(ass_library_t *priv, char **list); + +void ass_process_force_style(ass_track_t *track); /** * \brief initialize the renderer * \param priv library handle * \return renderer handle or NULL if failed */ -ass_renderer_t* ass_renderer_init(ass_library_t*); +ass_renderer_t *ass_renderer_init(ass_library_t *); /** * \brief finalize the renderer * \param priv renderer handle */ -void ass_renderer_done(ass_renderer_t* priv); +void ass_renderer_done(ass_renderer_t *priv); -void ass_set_frame_size(ass_renderer_t* priv, int w, int h); -void ass_set_margins(ass_renderer_t* priv, int t, int b, int l, int r); -void ass_set_use_margins(ass_renderer_t* priv, int use); -void ass_set_aspect_ratio(ass_renderer_t* priv, double ar); -void ass_set_font_scale(ass_renderer_t* priv, double font_scale); -void ass_set_hinting(ass_renderer_t* priv, ass_hinting_t ht); -void ass_set_line_spacing(ass_renderer_t* priv, double line_spacing); +void ass_set_frame_size(ass_renderer_t *priv, int w, int h); +void ass_set_margins(ass_renderer_t *priv, int t, int b, int l, int r); +void ass_set_use_margins(ass_renderer_t *priv, int use); +void ass_set_aspect_ratio(ass_renderer_t *priv, double ar, double par); +void ass_set_font_scale(ass_renderer_t *priv, double font_scale); +void ass_set_hinting(ass_renderer_t *priv, ass_hinting_t ht); +void ass_set_line_spacing(ass_renderer_t *priv, double line_spacing); /** * \brief set font lookup defaults + * \param fc bool, use fontconfig? + * \param config path to fontconfig configuration file, or NULL. Only matters + * if fontconfig is used */ -int ass_set_fonts(ass_renderer_t* priv, const char* default_font, const char* default_family); - -/** - * \brief set font lookup defaults, don't use fontconfig even if it is available - */ -int ass_set_fonts_nofc(ass_renderer_t* priv, const char* default_font, const char* default_family); +int ass_set_fonts(ass_renderer_t *priv, const char *default_font, + const char *default_family, int fc, const char *config); /** * \brief render a frame, producing a list of ass_image_t @@ -106,7 +105,8 @@ int ass_set_fonts_nofc(ass_renderer_t* priv, const char* default_font, const ch * \param track subtitle track * \param now video timestamp in milliseconds */ -ass_image_t* ass_render_frame(ass_renderer_t *priv, ass_track_t* track, long long now, int* detect_change); +ass_image_t *ass_render_frame(ass_renderer_t *priv, ass_track_t *track, + long long now, int *detect_change); // The following functions operate on track objects and do not need an ass_renderer // @@ -115,27 +115,27 @@ ass_image_t* ass_render_frame(ass_renderer_t *priv, ass_track_t* track, long lon * \brief allocate a new empty track object * \return pointer to empty track */ -ass_track_t* ass_new_track(ass_library_t*); +ass_track_t *ass_new_track(ass_library_t *); /** * \brief deallocate track and all its child objects (styles and events) * \param track track to deallocate */ -void ass_free_track(ass_track_t* track); +void ass_free_track(ass_track_t *track); /** * \brief allocate new style * \param track track * \return newly allocated style id */ -int ass_alloc_style(ass_track_t* track); +int ass_alloc_style(ass_track_t *track); /** * \brief allocate new event * \param track track * \return newly allocated event id */ -int ass_alloc_event(ass_track_t* track); +int ass_alloc_event(ass_track_t *track); /** * \brief delete a style @@ -143,7 +143,7 @@ int ass_alloc_event(ass_track_t* track); * \param sid style id * Deallocates style data. Does not modify track->n_styles. */ -void ass_free_style(ass_track_t* track, int sid); +void ass_free_style(ass_track_t *track, int sid); /** * \brief delete an event @@ -151,7 +151,7 @@ void ass_free_style(ass_track_t* track, int sid); * \param eid event id * Deallocates event data. Does not modify track->n_events. */ -void ass_free_event(ass_track_t* track, int eid); +void ass_free_event(ass_track_t *track, int eid); /** * \brief Parse a chunk of subtitle stream data. @@ -159,7 +159,7 @@ void ass_free_event(ass_track_t* track, int eid); * \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_t *track, char *data, int size); /** * \brief Parse Codec Private section of subtitle stream @@ -167,7 +167,7 @@ void ass_process_data(ass_track_t* track, char* data, int size); * \param data string to parse * \param size length of data */ -void ass_process_codec_private(ass_track_t* track, char *data, int size); +void ass_process_codec_private(ass_track_t *track, char *data, int size); /** * \brief Parse a chunk of subtitle stream data. In Matroska, this contains exactly 1 event (or a commentary). @@ -177,16 +177,16 @@ void ass_process_codec_private(ass_track_t* track, char *data, int size); * \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); - -char* read_file_recode(char* fname, char* codepage, size_t* size); +void ass_process_chunk(ass_track_t *track, char *data, int size, + long long timecode, long long duration); /** * \brief Read subtitles from file. * \param fname file name * \return newly allocated track */ -ass_track_t* ass_read_file(ass_library_t* library, char* fname, char* codepage); +ass_track_t *ass_read_file(ass_library_t *library, char *fname, + char *codepage); /** * \brief Read subtitles from memory. @@ -195,13 +195,14 @@ ass_track_t* ass_read_file(ass_library_t* library, char* fname, char* codepage); * \param bufsize size of buffer * \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_t *ass_read_memory(ass_library_t *library, char *buf, + size_t bufsize, char *codepage); /** * \brief read styles from file into already initialized track * \return 0 on success */ -int ass_read_styles(ass_track_t* track, char* fname, char* codepage); +int ass_read_styles(ass_track_t *track, char *fname, char *codepage); /** * \brief Add a memory font. @@ -209,12 +210,13 @@ int ass_read_styles(ass_track_t* track, char* fname, char* codepage); * \param data binary font data * \param data_size data size */ -void ass_add_font(ass_library_t* library, char* name, char* data, int data_size); +void ass_add_font(ass_library_t *library, char *name, char *data, + int data_size); /** * \brief Remove all fonts stored in ass_library object */ -void ass_clear_fonts(ass_library_t* library); +void ass_clear_fonts(ass_library_t *library); /** * \brief Calculates timeshift from now to the start of some other subtitle event, depending on movement parameter @@ -224,6 +226,6 @@ void ass_clear_fonts(ass_library_t* library); * +2 means "the one after the next", -1 means "previous" * \return timeshift, ms */ -long long ass_step_sub(ass_track_t* track, long long now, int movement); +long long ass_step_sub(ass_track_t *track, long long now, int movement); -#endif /* LIBASS_ASS_H */ +#endif /* LIBASS_ASS_H */ diff --git a/aegisub/libass/ass_bitmap.c b/aegisub/libass/ass_bitmap.c index 61457f1fc..16d2b351e 100644 --- a/aegisub/libass/ass_bitmap.c +++ b/aegisub/libass/ass_bitmap.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 * @@ -27,195 +25,207 @@ #include #include FT_GLYPH_H -#include "mputils.h" +#include "ass_utils.h" #include "ass_bitmap.h" struct ass_synth_priv_s { - int tmp_w, tmp_h; - unsigned short* tmp; + int tmp_w, tmp_h; + unsigned short *tmp; - int g_r; - int g_w; + int g_r; + int g_w; - unsigned *g; - unsigned *gt2; + unsigned *g; + unsigned *gt2; - double radius; + double radius; }; static const unsigned int maxcolor = 255; static const unsigned base = 256; -static int generate_tables(ass_synth_priv_t* priv, double radius) +static int generate_tables(ass_synth_priv_t *priv, double radius) { - double A = log(1.0/base)/(radius*radius*2); - int mx, i; - double volume_diff, volume_factor = 0; - unsigned volume; + double A = log(1.0 / base) / (radius * radius * 2); + int mx, i; + double volume_diff, volume_factor = 0; + unsigned volume; - if (priv->radius == radius) - return 0; - else - priv->radius = radius; + if (priv->radius == radius) + return 0; + else + priv->radius = radius; - priv->g_r = ceil(radius); - priv->g_w = 2*priv->g_r+1; + priv->g_r = ceil(radius); + priv->g_w = 2 * priv->g_r + 1; - if (priv->g_r) { - priv->g = realloc(priv->g, priv->g_w * sizeof(unsigned)); - priv->gt2 = realloc(priv->gt2, 256 * priv->g_w * sizeof(unsigned)); - if (priv->g==NULL || priv->gt2==NULL) { - return -1; - } - } + if (priv->g_r) { + priv->g = realloc(priv->g, priv->g_w * sizeof(unsigned)); + priv->gt2 = realloc(priv->gt2, 256 * priv->g_w * sizeof(unsigned)); + if (priv->g == NULL || priv->gt2 == NULL) { + return -1; + } + } - if (priv->g_r) { - // gaussian curve with volume = 256 - for (volume_diff=10000000; volume_diff>0.0000001; volume_diff*=0.5){ - volume_factor+= volume_diff; - volume=0; - for (i = 0; ig_w; ++i) { - priv->g[i] = (unsigned)(exp(A * (i-priv->g_r)*(i-priv->g_r)) * volume_factor + .5); - volume+= priv->g[i]; - } - if(volume>256) volume_factor-= volume_diff; - } - volume=0; - for (i = 0; ig_w; ++i) { - priv->g[i] = (unsigned)(exp(A * (i-priv->g_r)*(i-priv->g_r)) * volume_factor + .5); - volume+= priv->g[i]; - } + if (priv->g_r) { + // gaussian curve with volume = 256 + for (volume_diff = 10000000; volume_diff > 0.0000001; + volume_diff *= 0.5) { + volume_factor += volume_diff; + volume = 0; + for (i = 0; i < priv->g_w; ++i) { + priv->g[i] = + (unsigned) (exp(A * (i - priv->g_r) * (i - priv->g_r)) * + volume_factor + .5); + volume += priv->g[i]; + } + if (volume > 256) + volume_factor -= volume_diff; + } + volume = 0; + for (i = 0; i < priv->g_w; ++i) { + priv->g[i] = + (unsigned) (exp(A * (i - priv->g_r) * (i - priv->g_r)) * + volume_factor + .5); + volume += priv->g[i]; + } - // gauss table: - for(mx=0;mxg_w;mx++){ - for(i=0;i<256;i++){ - priv->gt2[mx+i*priv->g_w] = i*priv->g[mx]; - } - } - } + // gauss table: + for (mx = 0; mx < priv->g_w; mx++) { + for (i = 0; i < 256; i++) { + priv->gt2[mx + i * priv->g_w] = i * priv->g[mx]; + } + } + } - return 0; + return 0; } -static void resize_tmp(ass_synth_priv_t* priv, int w, int h) +static void resize_tmp(ass_synth_priv_t *priv, int w, int h) { - if (priv->tmp_w >= w && priv->tmp_h >= h) - return; - if (priv->tmp_w == 0) - priv->tmp_w = 64; - if (priv->tmp_h == 0) - priv->tmp_h = 64; - while (priv->tmp_w < w) priv->tmp_w *= 2; - while (priv->tmp_h < h) priv->tmp_h *= 2; - if (priv->tmp) - free(priv->tmp); - priv->tmp = malloc((priv->tmp_w + 1) * priv->tmp_h * sizeof(short)); + if (priv->tmp_w >= w && priv->tmp_h >= h) + return; + if (priv->tmp_w == 0) + priv->tmp_w = 64; + if (priv->tmp_h == 0) + priv->tmp_h = 64; + while (priv->tmp_w < w) + priv->tmp_w *= 2; + while (priv->tmp_h < h) + priv->tmp_h *= 2; + if (priv->tmp) + free(priv->tmp); + priv->tmp = malloc((priv->tmp_w + 1) * priv->tmp_h * sizeof(short)); } -ass_synth_priv_t* ass_synth_init(double radius) +ass_synth_priv_t *ass_synth_init(double radius) { - ass_synth_priv_t* priv = calloc(1, sizeof(ass_synth_priv_t)); - generate_tables(priv, radius); - return priv; + ass_synth_priv_t *priv = calloc(1, sizeof(ass_synth_priv_t)); + generate_tables(priv, radius); + return priv; } -void ass_synth_done(ass_synth_priv_t* priv) +void ass_synth_done(ass_synth_priv_t *priv) { - if (priv->tmp) - free(priv->tmp); - if (priv->g) - free(priv->g); - if (priv->gt2) - free(priv->gt2); - free(priv); + if (priv->tmp) + free(priv->tmp); + if (priv->g) + free(priv->g); + if (priv->gt2) + free(priv->gt2); + free(priv); } -static bitmap_t* alloc_bitmap(int w, int h) +static bitmap_t *alloc_bitmap(int w, int h) { - bitmap_t* bm; - bm = calloc(1, sizeof(bitmap_t)); - bm->buffer = malloc(w*h); - bm->w = w; - bm->h = h; - bm->left = bm->top = 0; - return bm; + bitmap_t *bm; + bm = calloc(1, sizeof(bitmap_t)); + bm->buffer = malloc(w * h); + bm->w = w; + bm->h = h; + bm->left = bm->top = 0; + return bm; } -void ass_free_bitmap(bitmap_t* bm) +void ass_free_bitmap(bitmap_t *bm) { - if (bm) { - if (bm->buffer) free(bm->buffer); - free(bm); - } + if (bm) { + if (bm->buffer) + free(bm->buffer); + free(bm); + } } -static bitmap_t* copy_bitmap(const bitmap_t* src) +static bitmap_t *copy_bitmap(const bitmap_t *src) { - bitmap_t* dst = alloc_bitmap(src->w, src->h); - dst->left = src->left; - dst->top = src->top; - memcpy(dst->buffer, src->buffer, src->w * src->h); - return dst; + bitmap_t *dst = alloc_bitmap(src->w, src->h); + dst->left = src->left; + dst->top = src->top; + memcpy(dst->buffer, src->buffer, src->w * src->h); + return dst; } static int check_glyph_area(FT_Glyph glyph) { - FT_BBox bbox; - long long dx, dy; - FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox); - dx = bbox.xMax - bbox.xMin; - dy = bbox.yMax - bbox.yMin; - if (dx * dy > 8000000) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_GlyphBBoxTooLarge, (int)dx, (int)dy); - return 1; - } else - return 0; + FT_BBox bbox; + long long dx, dy; + FT_Glyph_Get_CBox(glyph, FT_GLYPH_BBOX_TRUNCATE, &bbox); + dx = bbox.xMax - bbox.xMin; + dy = bbox.yMax - bbox.yMin; + if (dx * dy > 8000000) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_GlyphBBoxTooLarge, + (int) dx, (int) dy); + return 1; + } else + return 0; } -static bitmap_t* glyph_to_bitmap_internal(FT_Glyph glyph, int bord) +static bitmap_t *glyph_to_bitmap_internal(FT_Glyph glyph, int bord) { - FT_BitmapGlyph bg; - FT_Bitmap* bit; - bitmap_t* bm; - int w, h; - unsigned char* src; - unsigned char* dst; - int i; - int error; + FT_BitmapGlyph bg; + FT_Bitmap *bit; + bitmap_t *bm; + int w, h; + unsigned char *src; + unsigned char *dst; + int i; + int error; - if (check_glyph_area(glyph)) - return 0; - error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 0); - if (error) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FT_Glyph_To_BitmapError, error); - return 0; - } + if (check_glyph_area(glyph)) + return 0; + error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 0); + if (error) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_FT_Glyph_To_BitmapError, + error); + return 0; + } - bg = (FT_BitmapGlyph)glyph; - bit = &(bg->bitmap); - if (bit->pixel_mode != FT_PIXEL_MODE_GRAY) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UnsupportedPixelMode, (int)(bit->pixel_mode)); - FT_Done_Glyph(glyph); - return 0; - } + bg = (FT_BitmapGlyph) glyph; + bit = &(bg->bitmap); + if (bit->pixel_mode != FT_PIXEL_MODE_GRAY) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_UnsupportedPixelMode, + (int) (bit->pixel_mode)); + FT_Done_Glyph(glyph); + return 0; + } - w = bit->width; - h = bit->rows; - bm = alloc_bitmap(w + 2*bord, h + 2*bord); - memset(bm->buffer, 0, bm->w * bm->h); - bm->left = bg->left - bord; - bm->top = - bg->top - bord; + w = bit->width; + h = bit->rows; + bm = alloc_bitmap(w + 2 * bord, h + 2 * bord); + memset(bm->buffer, 0, bm->w * bm->h); + bm->left = bg->left - bord; + bm->top = -bg->top - bord; - src = bit->buffer; - dst = bm->buffer + bord + bm->w * bord; - for (i = 0; i < h; ++i) { - memcpy(dst, src, w); - src += bit->pitch; - dst += bm->w; - } + src = bit->buffer; + dst = bm->buffer + bord + bm->w * bord; + for (i = 0; i < h; ++i) { + memcpy(dst, src, w); + src += bit->pitch; + dst += bm->w; + } - FT_Done_Glyph(glyph); - return bm; + FT_Done_Glyph(glyph); + return bm; } /** @@ -224,114 +234,183 @@ static bitmap_t* glyph_to_bitmap_internal(FT_Glyph glyph, int bord) * 1. Glyph bitmap is subtracted from outline bitmap. This way looks much better in some cases. * 2. Shadow bitmap is created as a sum of glyph and outline bitmaps. */ -static bitmap_t* fix_outline_and_shadow(bitmap_t* bm_g, bitmap_t* bm_o) +static bitmap_t *fix_outline_and_shadow(bitmap_t *bm_g, bitmap_t *bm_o) { - int x, y; - const int l = bm_o->left > bm_g->left ? bm_o->left : bm_g->left; - const int t = bm_o->top > bm_g->top ? bm_o->top : bm_g->top; - const int r = bm_o->left + bm_o->w < bm_g->left + bm_g->w ? bm_o->left + bm_o->w : bm_g->left + bm_g->w; - const int b = bm_o->top + bm_o->h < bm_g->top + bm_g->h ? bm_o->top + bm_o->h : bm_g->top + bm_g->h; + int x, y; + const int l = bm_o->left > bm_g->left ? bm_o->left : bm_g->left; + const int t = bm_o->top > bm_g->top ? bm_o->top : bm_g->top; + const int r = + bm_o->left + bm_o->w < + bm_g->left + bm_g->w ? bm_o->left + bm_o->w : bm_g->left + bm_g->w; + const int b = + bm_o->top + bm_o->h < + bm_g->top + bm_g->h ? bm_o->top + bm_o->h : bm_g->top + bm_g->h; - bitmap_t* bm_s = copy_bitmap(bm_o); + bitmap_t *bm_s = copy_bitmap(bm_o); - unsigned char* g = bm_g->buffer + (t - bm_g->top) * bm_g->w + (l - bm_g->left); - unsigned char* o = bm_o->buffer + (t - bm_o->top) * bm_o->w + (l - bm_o->left); - unsigned char* s = bm_s->buffer + (t - bm_s->top) * bm_s->w + (l - bm_s->left); - - for (y = 0; y < b - t; ++y) { - for (x = 0; x < r - l; ++x) { - unsigned char c_g, c_o; - c_g = g[x]; - c_o = o[x]; - o[x] = (c_o > c_g) ? c_o - (c_g/2) : 0; - s[x] = (c_o < 0xFF - c_g) ? c_o + c_g : 0xFF; - } - g += bm_g->w; - o += bm_o->w; - s += bm_s->w; - } + unsigned char *g = + bm_g->buffer + (t - bm_g->top) * bm_g->w + (l - bm_g->left); + unsigned char *o = + bm_o->buffer + (t - bm_o->top) * bm_o->w + (l - bm_o->left); + unsigned char *s = + bm_s->buffer + (t - bm_s->top) * bm_s->w + (l - bm_s->left); - assert(bm_s); - return bm_s; + for (y = 0; y < b - t; ++y) { + for (x = 0; x < r - l; ++x) { + unsigned char c_g, c_o; + c_g = g[x]; + c_o = o[x]; + o[x] = (c_o > c_g) ? c_o - (c_g / 2) : 0; + s[x] = (c_o < 0xFF - c_g) ? c_o + c_g : 0xFF; + } + g += bm_g->w; + o += bm_o->w; + s += bm_s->w; + } + + assert(bm_s); + return bm_s; +} + +/** + * \brief Shift a bitmap by the fraction of a pixel in x and y direction + * expressed in 26.6 fixed point + */ +static void shift_bitmap(unsigned char *buf, int w, int h, int shift_x, + int shift_y) +{ + int x, y, b; + + // Shift in x direction + if (shift_x > 0) { + for (y = 0; y < h; y++) { + for (x = w - 1; x > 0; x--) { + b = (buf[x + y * w - 1] * shift_x) >> 6; + buf[x + y * w - 1] -= b; + buf[x + y * w] += b; + } + } + } else if (shift_x < 0) { + shift_x = -shift_x; + for (y = 0; y < h; y++) { + for (x = 0; x < w - 1; x++) { + b = (buf[x + y * w + 1] * shift_x) >> 6; + buf[x + y * w + 1] -= b; + buf[x + y * w] += b; + } + } + } + + // Shift in y direction + if (shift_y > 0) { + for (x = 0; x < w; x++) { + for (y = h - 1; y > 0; y--) { + b = (buf[x + (y - 1) * w] * shift_y) >> 6; + buf[x + (y - 1) * w] -= b; + buf[x + y * w] += b; + } + } + } else if (shift_y < 0) { + shift_y = -shift_y; + for (x = 0; x < w; x++) { + for (y = 0; y < h - 1; y++) { + b = (buf[x + (y + 1) * w] * shift_y) >> 6; + buf[x + (y + 1) * w] -= b; + buf[x + y * w] += b; + } + } + } } /** * \brief Blur with [[1,2,1]. [2,4,2], [1,2,1]] kernel * This blur is the same as the one employed by vsfilter. */ -static void be_blur(unsigned char *buf, int w, int h) { - unsigned int x, y; - unsigned int old_sum, new_sum; - - for (y=0; y> 2; - old_sum = new_sum; - } - } - - for (x=0; x> 2; - old_sum = new_sum; - } - } -} - -int glyph_to_bitmap(ass_synth_priv_t* priv_blur, - FT_Glyph glyph, FT_Glyph outline_glyph, bitmap_t** bm_g, - bitmap_t** bm_o, bitmap_t** bm_s, int be, double blur_radius) +static void be_blur(unsigned char *buf, int w, int h) { - int bord = be ? (be/4+1) : 0; - blur_radius *= 2; - bord = (blur_radius > 0.0) ? blur_radius : bord; + unsigned int x, y; + unsigned int old_sum, new_sum; - assert(bm_g && bm_o && bm_s); + for (y = 0; y < h; y++) { + old_sum = 2 * buf[y * w]; + for (x = 0; x < w - 1; x++) { + new_sum = buf[y * w + x] + buf[y * w + x + 1]; + buf[y * w + x] = (old_sum + new_sum) >> 2; + old_sum = new_sum; + } + } - *bm_g = *bm_o = *bm_s = 0; - - if (glyph) - *bm_g = glyph_to_bitmap_internal(glyph, bord); - if (!*bm_g) - return 1; - - if (outline_glyph) { - *bm_o = glyph_to_bitmap_internal(outline_glyph, bord); - if (!*bm_o) { - ass_free_bitmap(*bm_g); - return 1; - } - } - if (*bm_o) - resize_tmp(priv_blur, (*bm_o)->w, (*bm_o)->h); - resize_tmp(priv_blur, (*bm_g)->w, (*bm_g)->h); - - if (be) { - while (be--) { - if (*bm_o) - be_blur((*bm_o)->buffer, (*bm_o)->w, (*bm_o)->h); - else - be_blur((*bm_g)->buffer, (*bm_g)->w, (*bm_g)->h); - } - } else { - if (blur_radius > 0.0) { - generate_tables(priv_blur, blur_radius); - if (*bm_o) - blur((*bm_o)->buffer, priv_blur->tmp, (*bm_o)->w, (*bm_o)->h, (*bm_o)->w, (int*)priv_blur->gt2, priv_blur->g_r, priv_blur->g_w); - else - blur((*bm_g)->buffer, priv_blur->tmp, (*bm_g)->w, (*bm_g)->h, (*bm_g)->w, (int*)priv_blur->gt2, priv_blur->g_r, priv_blur->g_w); - } - } - if (*bm_o) - *bm_s = fix_outline_and_shadow(*bm_g, *bm_o); - else - *bm_s = copy_bitmap(*bm_g); - - assert(bm_s); - return 0; + for (x = 0; x < w; x++) { + old_sum = 2 * buf[x]; + for (y = 0; y < h - 1; y++) { + new_sum = buf[y * w + x] + buf[(y + 1) * w + x]; + buf[y * w + x] = (old_sum + new_sum) >> 2; + old_sum = new_sum; + } + } } +int glyph_to_bitmap(ass_synth_priv_t *priv_blur, + FT_Glyph glyph, FT_Glyph outline_glyph, + bitmap_t **bm_g, bitmap_t **bm_o, bitmap_t **bm_s, + int be, double blur_radius, FT_Vector shadow_offset) +{ + int bord = be ? (be / 4 + 1) : 0; + blur_radius *= 2; + bord = (blur_radius > 0.0) ? blur_radius + 1 : bord; + if (bord == 0 && (shadow_offset.x || shadow_offset.y)) + bord = 1; + + assert(bm_g && bm_o && bm_s); + + *bm_g = *bm_o = *bm_s = 0; + + if (glyph) + *bm_g = glyph_to_bitmap_internal(glyph, bord); + if (!*bm_g) + return 1; + + if (outline_glyph) { + *bm_o = glyph_to_bitmap_internal(outline_glyph, bord); + if (!*bm_o) { + ass_free_bitmap(*bm_g); + return 1; + } + } + if (*bm_o) + resize_tmp(priv_blur, (*bm_o)->w, (*bm_o)->h); + resize_tmp(priv_blur, (*bm_g)->w, (*bm_g)->h); + + if (be) { + while (be--) { + if (*bm_o) + be_blur((*bm_o)->buffer, (*bm_o)->w, (*bm_o)->h); + else + be_blur((*bm_g)->buffer, (*bm_g)->w, (*bm_g)->h); + } + } else { + if (blur_radius > 0.0) { + generate_tables(priv_blur, blur_radius); + if (*bm_o) + ass_gauss_blur((*bm_o)->buffer, priv_blur->tmp, + (*bm_o)->w, (*bm_o)->h, (*bm_o)->w, + (int *) priv_blur->gt2, priv_blur->g_r, + priv_blur->g_w); + else + ass_gauss_blur((*bm_g)->buffer, priv_blur->tmp, + (*bm_g)->w, (*bm_g)->h, (*bm_g)->w, + (int *) priv_blur->gt2, priv_blur->g_r, + priv_blur->g_w); + } + } + if (*bm_o) + *bm_s = fix_outline_and_shadow(*bm_g, *bm_o); + else + *bm_s = copy_bitmap(*bm_g); + + shift_bitmap((*bm_s)->buffer, (*bm_s)->w,(*bm_s)->h, + shadow_offset.x, shadow_offset.y); + + assert(bm_s); + return 0; +} diff --git a/aegisub/libass/ass_bitmap.h b/aegisub/libass/ass_bitmap.h index c0b4ad201..f6b138058 100644 --- a/aegisub/libass/ass_bitmap.h +++ b/aegisub/libass/ass_bitmap.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 * @@ -28,13 +26,13 @@ typedef struct ass_synth_priv_s ass_synth_priv_t; -ass_synth_priv_t* ass_synth_init(double); -void ass_synth_done(ass_synth_priv_t* priv); +ass_synth_priv_t *ass_synth_init(double); +void ass_synth_done(ass_synth_priv_t *priv); typedef struct bitmap_s { - int left, top; - int w, h; // width, height - unsigned char* buffer; // w x h buffer + int left, top; + int w, h; // width, height + unsigned char *buffer; // w x h buffer } bitmap_t; /** @@ -46,8 +44,11 @@ typedef struct bitmap_s { * \param bm_g out: pointer to the bitmap of glyph shadow is returned here * \param be 1 = produces blurred bitmaps, 0 = normal bitmaps */ -int glyph_to_bitmap(ass_synth_priv_t* priv_blur, FT_Glyph glyph, FT_Glyph outline_glyph, bitmap_t** bm_g, bitmap_t** bm_o, bitmap_t** bm_s, int be, double blur_radius); +int glyph_to_bitmap(ass_synth_priv_t *priv_blur, FT_Glyph glyph, + FT_Glyph outline_glyph, bitmap_t **bm_g, + bitmap_t **bm_o, bitmap_t **bm_s, int be, + double blur_radius, FT_Vector shadow_offset); -void ass_free_bitmap(bitmap_t* bm); +void ass_free_bitmap(bitmap_t *bm); -#endif /* LIBASS_BITMAP_H */ +#endif /* LIBASS_BITMAP_H */ diff --git a/aegisub/libass/ass_cache.c b/aegisub/libass/ass_cache.c index 2c8ee26f8..fc70b0d4f 100644 --- a/aegisub/libass/ass_cache.c +++ b/aegisub/libass/ass_cache.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 * @@ -29,205 +27,170 @@ #include -#include "mputils.h" +#include "ass_utils.h" #include "ass.h" #include "ass_fontconfig.h" #include "ass_font.h" #include "ass_bitmap.h" #include "ass_cache.h" - -typedef struct hashmap_item_s { - void* key; - void* value; - struct hashmap_item_s* next; -} hashmap_item_t; -typedef hashmap_item_t* hashmap_item_p; - -struct hashmap_s { - int nbuckets; - size_t key_size, value_size; - hashmap_item_p* root; - hashmap_item_dtor_t item_dtor; // a destructor for hashmap key/value pairs - hashmap_key_compare_t key_compare; - hashmap_hash_t hash; - // stats - int hit_count; - int miss_count; - int count; -}; - -#define FNV1_32A_INIT (unsigned)0x811c9dc5 - -static inline unsigned fnv_32a_buf(void* buf, size_t len, unsigned hval) +static unsigned hashmap_hash(void *buf, size_t len) { - unsigned char *bp = buf; - unsigned char *be = bp + len; - while (bp < be) { - hval ^= (unsigned)*bp++; - hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); - } - return hval; -} -static inline unsigned fnv_32a_str(char* str, unsigned hval) -{ - unsigned char* s = (unsigned char*)str; - while (*s) { - hval ^= (unsigned)*s++; - hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); - } - return hval; + return fnv_32a_buf(buf, len, FNV1_32A_INIT); } -static unsigned hashmap_hash(void* buf, size_t len) +static int hashmap_key_compare(void *a, void *b, size_t size) { - return fnv_32a_buf(buf, len, FNV1_32A_INIT); + return memcmp(a, b, size) == 0; } -static int hashmap_key_compare(void* a, void* b, size_t size) +static void hashmap_item_dtor(void *key, size_t key_size, void *value, + size_t value_size) { - return memcmp(a, b, size) == 0; + free(key); + free(value); } -static void hashmap_item_dtor(void* key, size_t key_size, void* value, size_t value_size) +hashmap_t *hashmap_init(size_t key_size, size_t value_size, int nbuckets, + hashmap_item_dtor_t item_dtor, + hashmap_key_compare_t key_compare, + hashmap_hash_t hash) { - free(key); - free(value); + hashmap_t *map = calloc(1, sizeof(hashmap_t)); + map->nbuckets = nbuckets; + map->key_size = key_size; + map->value_size = value_size; + map->root = calloc(nbuckets, sizeof(hashmap_item_p)); + map->item_dtor = item_dtor ? item_dtor : hashmap_item_dtor; + map->key_compare = key_compare ? key_compare : hashmap_key_compare; + map->hash = hash ? hash : hashmap_hash; + return map; } -hashmap_t* hashmap_init(size_t key_size, size_t value_size, int nbuckets, - hashmap_item_dtor_t item_dtor, hashmap_key_compare_t key_compare, - hashmap_hash_t hash) +void hashmap_done(hashmap_t *map) { - hashmap_t* map = calloc(1, sizeof(hashmap_t)); - map->nbuckets = nbuckets; - map->key_size = key_size; - map->value_size = value_size; - map->root = calloc(nbuckets, sizeof(hashmap_item_p)); - map->item_dtor = item_dtor ? item_dtor : hashmap_item_dtor; - map->key_compare = key_compare ? key_compare : hashmap_key_compare; - map->hash = hash ? hash : hashmap_hash; - return map; -} + int i; + // print stats + if (map->count > 0 || map->hit_count + map->miss_count > 0) + ass_msg(MSGL_V, + "cache statistics: \n total accesses: %d\n hits: %d\n misses: %d\n object count: %d\n", + map->hit_count + map->miss_count, map->hit_count, + map->miss_count, map->count); -void hashmap_done(hashmap_t* map) -{ - int i; - // print stats - if (map->count > 0 || map->hit_count + map->miss_count > 0) - mp_msg(MSGT_ASS, MSGL_V, "cache statistics: \n total accesses: %d\n hits: %d\n misses: %d\n object count: %d\n", - map->hit_count + map->miss_count, map->hit_count, map->miss_count, map->count); - - for (i = 0; i < map->nbuckets; ++i) { - hashmap_item_t* item = map->root[i]; - while (item) { - hashmap_item_t* next = item->next; - map->item_dtor(item->key, map->key_size, item->value, map->value_size); - free(item); - item = next; - } - } - free(map->root); - free(map); + for (i = 0; i < map->nbuckets; ++i) { + hashmap_item_t *item = map->root[i]; + while (item) { + hashmap_item_t *next = item->next; + map->item_dtor(item->key, map->key_size, item->value, + map->value_size); + free(item); + item = next; + } + } + free(map->root); + free(map); } // does nothing if key already exists -void* hashmap_insert(hashmap_t* map, void* key, void* value) +void *hashmap_insert(hashmap_t *map, void *key, void *value) { - unsigned hash = map->hash(key, map->key_size); - hashmap_item_t** next = map->root + (hash % map->nbuckets); - while (*next) { - if (map->key_compare(key, (*next)->key, map->key_size)) - return (*next)->value; - next = &((*next)->next); - assert(next); - } - (*next) = malloc(sizeof(hashmap_item_t)); - (*next)->key = malloc(map->key_size); - (*next)->value = malloc(map->value_size); - memcpy((*next)->key, key, map->key_size); - memcpy((*next)->value, value, map->value_size); - (*next)->next = 0; + unsigned hash = map->hash(key, map->key_size); + hashmap_item_t **next = map->root + (hash % map->nbuckets); + while (*next) { + if (map->key_compare(key, (*next)->key, map->key_size)) + return (*next)->value; + next = &((*next)->next); + assert(next); + } + (*next) = malloc(sizeof(hashmap_item_t)); + (*next)->key = malloc(map->key_size); + (*next)->value = malloc(map->value_size); + memcpy((*next)->key, key, map->key_size); + memcpy((*next)->value, value, map->value_size); + (*next)->next = 0; - map->count ++; - return (*next)->value; + map->count++; + return (*next)->value; } -void* hashmap_find(hashmap_t* map, void* key) +void *hashmap_find(hashmap_t *map, void *key) { - unsigned hash = map->hash(key, map->key_size); - hashmap_item_t* item = map->root[hash % map->nbuckets]; - while (item) { - if (map->key_compare(key, item->key, map->key_size)) { - map->hit_count++; - return item->value; - } - item = item->next; - } - map->miss_count++; - return 0; + unsigned hash = map->hash(key, map->key_size); + hashmap_item_t *item = map->root[hash % map->nbuckets]; + while (item) { + if (map->key_compare(key, item->key, map->key_size)) { + map->hit_count++; + return item->value; + } + item = item->next; + } + map->miss_count++; + return 0; } //--------------------------------- // font cache -hashmap_t* font_cache; - -static unsigned font_desc_hash(void* buf, size_t len) +static unsigned font_desc_hash(void *buf, size_t len) { - ass_font_desc_t* desc = buf; - unsigned hval; - hval = fnv_32a_str(desc->family, FNV1_32A_INIT); - hval = fnv_32a_buf(&desc->bold, sizeof(desc->bold), hval); - hval = fnv_32a_buf(&desc->italic, sizeof(desc->italic), hval); - return hval; + ass_font_desc_t *desc = buf; + unsigned hval; + hval = fnv_32a_str(desc->family, FNV1_32A_INIT); + hval = fnv_32a_buf(&desc->bold, sizeof(desc->bold), hval); + hval = fnv_32a_buf(&desc->italic, sizeof(desc->italic), hval); + return hval; } -static int font_compare(void* key1, void* key2, size_t key_size) { - ass_font_desc_t* a = key1; - ass_font_desc_t* b = key2; - if (strcmp(a->family, b->family) != 0) - return 0; - if (a->bold != b->bold) - return 0; - if (a->italic != b->italic) - return 0; - if (a->treat_family_as_pattern != b->treat_family_as_pattern) - return 0; - return 1; +static int font_compare(void *key1, void *key2, size_t key_size) +{ + ass_font_desc_t *a = key1; + ass_font_desc_t *b = key2; + if (strcmp(a->family, b->family) != 0) + return 0; + if (a->bold != b->bold) + return 0; + if (a->italic != b->italic) + return 0; + if (a->treat_family_as_pattern != b->treat_family_as_pattern) + return 0; + return 1; } -static void font_hash_dtor(void* key, size_t key_size, void* value, size_t value_size) +static void font_hash_dtor(void *key, size_t key_size, void *value, + size_t value_size) { - ass_font_free(value); - free(key); + ass_font_free(value); + free(key); } -ass_font_t* ass_font_cache_find(ass_font_desc_t* desc) +ass_font_t *ass_font_cache_find(hashmap_t *font_cache, + ass_font_desc_t *desc) { - return hashmap_find(font_cache, desc); + return hashmap_find(font_cache, desc); } /** * \brief Add a face struct to cache. * \param font font struct */ -void* ass_font_cache_add(ass_font_t* font) +void *ass_font_cache_add(hashmap_t *font_cache, ass_font_t *font) { - return hashmap_insert(font_cache, &(font->desc), font); + return hashmap_insert(font_cache, &(font->desc), font); } -void ass_font_cache_init(void) +hashmap_t *ass_font_cache_init(void) { - font_cache = hashmap_init(sizeof(ass_font_desc_t), - sizeof(ass_font_t), - 1000, - font_hash_dtor, font_compare, font_desc_hash); + hashmap_t *font_cache; + font_cache = hashmap_init(sizeof(ass_font_desc_t), + sizeof(ass_font_t), + 1000, + font_hash_dtor, font_compare, font_desc_hash); + return font_cache; } -void ass_font_cache_done(void) +void ass_font_cache_done(hashmap_t *font_cache) { - hashmap_done(font_cache); + hashmap_done(font_cache); } @@ -240,119 +203,131 @@ void ass_font_cache_done(void) //--------------------------------- // bitmap cache -hashmap_t* bitmap_cache; - -static void bitmap_hash_dtor(void* key, size_t key_size, void* value, size_t value_size) +static void bitmap_hash_dtor(void *key, size_t key_size, void *value, + size_t value_size) { - bitmap_hash_val_t* v = value; - if (v->bm) ass_free_bitmap(v->bm); - if (v->bm_o) ass_free_bitmap(v->bm_o); - if (v->bm_s) ass_free_bitmap(v->bm_s); - free(key); - free(value); + bitmap_hash_val_t *v = value; + if (v->bm) + ass_free_bitmap(v->bm); + if (v->bm_o) + ass_free_bitmap(v->bm_o); + if (v->bm_s) + ass_free_bitmap(v->bm_s); + free(key); + free(value); } -void* cache_add_bitmap(bitmap_hash_key_t* key, bitmap_hash_val_t* val) +void *cache_add_bitmap(hashmap_t *bitmap_cache, bitmap_hash_key_t *key, + bitmap_hash_val_t *val) { - return hashmap_insert(bitmap_cache, key, val); + return hashmap_insert(bitmap_cache, key, val); } /** * \brief Get a bitmap from bitmap cache. * \param key hash key * \return requested hash val or 0 if not found -*/ -bitmap_hash_val_t* cache_find_bitmap(bitmap_hash_key_t* key) +*/ +bitmap_hash_val_t *cache_find_bitmap(hashmap_t *bitmap_cache, + bitmap_hash_key_t *key) { - return hashmap_find(bitmap_cache, key); + return hashmap_find(bitmap_cache, key); } -void ass_bitmap_cache_init(void) +hashmap_t *ass_bitmap_cache_init(void) { - bitmap_cache = hashmap_init(sizeof(bitmap_hash_key_t), - sizeof(bitmap_hash_val_t), - 0xFFFF + 13, - bitmap_hash_dtor, bitmap_compare, - bitmap_hash); + hashmap_t *bitmap_cache; + bitmap_cache = hashmap_init(sizeof(bitmap_hash_key_t), + sizeof(bitmap_hash_val_t), + 0xFFFF + 13, + bitmap_hash_dtor, bitmap_compare, + bitmap_hash); + return bitmap_cache; } -void ass_bitmap_cache_done(void) +void ass_bitmap_cache_done(hashmap_t *bitmap_cache) { - hashmap_done(bitmap_cache); + hashmap_done(bitmap_cache); } -void ass_bitmap_cache_reset(void) +hashmap_t *ass_bitmap_cache_reset(hashmap_t *bitmap_cache) { - ass_bitmap_cache_done(); - ass_bitmap_cache_init(); + ass_bitmap_cache_done(bitmap_cache); + return ass_bitmap_cache_init(); } //--------------------------------- // glyph cache -hashmap_t* glyph_cache; - -static void glyph_hash_dtor(void* key, size_t key_size, void* value, size_t value_size) +static void glyph_hash_dtor(void *key, size_t key_size, void *value, + size_t value_size) { - glyph_hash_val_t* v = value; - if (v->glyph) FT_Done_Glyph(v->glyph); - if (v->outline_glyph) FT_Done_Glyph(v->outline_glyph); - free(key); - free(value); + glyph_hash_val_t *v = value; + if (v->glyph) + FT_Done_Glyph(v->glyph); + if (v->outline_glyph) + FT_Done_Glyph(v->outline_glyph); + free(key); + free(value); } -void* cache_add_glyph(glyph_hash_key_t* key, glyph_hash_val_t* val) +void *cache_add_glyph(hashmap_t *glyph_cache, glyph_hash_key_t *key, + glyph_hash_val_t *val) { - return hashmap_insert(glyph_cache, key, val); + return hashmap_insert(glyph_cache, key, val); } /** * \brief Get a glyph from glyph cache. * \param key hash key * \return requested hash val or 0 if not found -*/ -glyph_hash_val_t* cache_find_glyph(glyph_hash_key_t* key) +*/ +glyph_hash_val_t *cache_find_glyph(hashmap_t *glyph_cache, + glyph_hash_key_t *key) { - return hashmap_find(glyph_cache, key); + return hashmap_find(glyph_cache, key); } -void ass_glyph_cache_init(void) +hashmap_t *ass_glyph_cache_init(void) { - glyph_cache = hashmap_init(sizeof(glyph_hash_key_t), - sizeof(glyph_hash_val_t), - 0xFFFF + 13, - glyph_hash_dtor, glyph_compare, glyph_hash); + hashmap_t *glyph_cache; + glyph_cache = hashmap_init(sizeof(glyph_hash_key_t), + sizeof(glyph_hash_val_t), + 0xFFFF + 13, + glyph_hash_dtor, glyph_compare, glyph_hash); + return glyph_cache; } -void ass_glyph_cache_done(void) +void ass_glyph_cache_done(hashmap_t *glyph_cache) { - hashmap_done(glyph_cache); + hashmap_done(glyph_cache); } -void ass_glyph_cache_reset(void) +hashmap_t *ass_glyph_cache_reset(hashmap_t *glyph_cache) { - ass_glyph_cache_done(); - ass_glyph_cache_init(); + ass_glyph_cache_done(glyph_cache); + return ass_glyph_cache_init(); } //--------------------------------- // composite cache -hashmap_t* composite_cache; - -static void composite_hash_dtor(void* key, size_t key_size, void* value, size_t value_size) +static void composite_hash_dtor(void *key, size_t key_size, void *value, + size_t value_size) { - composite_hash_val_t* v = value; - free(v->a); - free(v->b); - free(key); - free(value); + composite_hash_val_t *v = value; + free(v->a); + free(v->b); + free(key); + free(value); } -void* cache_add_composite(composite_hash_key_t* key, composite_hash_val_t* val) +void *cache_add_composite(hashmap_t *composite_cache, + composite_hash_key_t *key, + composite_hash_val_t *val) { - return hashmap_insert(composite_cache, key, val); + return hashmap_insert(composite_cache, key, val); } /** @@ -360,27 +335,30 @@ void* cache_add_composite(composite_hash_key_t* key, composite_hash_val_t* val) * \param key hash key * \return requested hash val or 0 if not found */ -composite_hash_val_t* cache_find_composite(composite_hash_key_t* key) +composite_hash_val_t *cache_find_composite(hashmap_t *composite_cache, + composite_hash_key_t *key) { - return hashmap_find(composite_cache, key); + return hashmap_find(composite_cache, key); } -void ass_composite_cache_init(void) +hashmap_t *ass_composite_cache_init(void) { - composite_cache = hashmap_init(sizeof(composite_hash_key_t), - sizeof(composite_hash_val_t), - 0xFFFF + 13, - composite_hash_dtor, NULL, NULL); + hashmap_t *composite_cache; + composite_cache = hashmap_init(sizeof(composite_hash_key_t), + sizeof(composite_hash_val_t), + 0xFFFF + 13, + composite_hash_dtor, composite_compare, + composite_hash); + return composite_cache; } -void ass_composite_cache_done(void) +void ass_composite_cache_done(hashmap_t *composite_cache) { - hashmap_done(composite_cache); + hashmap_done(composite_cache); } -void ass_composite_cache_reset(void) +hashmap_t *ass_composite_cache_reset(hashmap_t *composite_cache) { - ass_composite_cache_done(); - ass_composite_cache_init(); + ass_composite_cache_done(composite_cache); + return ass_composite_cache_init(); } - diff --git a/aegisub/libass/ass_cache.h b/aegisub/libass/ass_cache.h index bad0ed8e6..d8de97a18 100644 --- a/aegisub/libass/ass_cache.h +++ b/aegisub/libass/ass_cache.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 * @@ -27,72 +25,92 @@ #include "ass_font.h" #include "ass_bitmap.h" -void ass_font_cache_init(void); -ass_font_t* ass_font_cache_find(ass_font_desc_t* desc); -void* ass_font_cache_add(ass_font_t* font); -void ass_font_cache_done(void); +typedef void (*hashmap_item_dtor_t) (void *key, size_t key_size, + void *value, size_t value_size); +typedef int (*hashmap_key_compare_t) (void *key1, void *key2, + size_t key_size); +typedef unsigned (*hashmap_hash_t) (void *key, size_t key_size); +typedef struct hashmap_item_s { + void *key; + void *value; + struct hashmap_item_s *next; +} hashmap_item_t; +typedef hashmap_item_t *hashmap_item_p; + +typedef struct hashmap_s { + int nbuckets; + size_t key_size, value_size; + hashmap_item_p *root; + hashmap_item_dtor_t item_dtor; // a destructor for hashmap key/value pairs + hashmap_key_compare_t key_compare; + hashmap_hash_t hash; + // stats + int hit_count; + int miss_count; + int count; +} hashmap_t; + +hashmap_t *hashmap_init(size_t key_size, size_t value_size, int nbuckets, + hashmap_item_dtor_t item_dtor, + hashmap_key_compare_t key_compare, + hashmap_hash_t hash); +void hashmap_done(hashmap_t *map); +void *hashmap_insert(hashmap_t *map, void *key, void *value); +void *hashmap_find(hashmap_t *map, void *key); + +hashmap_t *ass_font_cache_init(void); +ass_font_t *ass_font_cache_find(hashmap_t *, ass_font_desc_t *desc); +void *ass_font_cache_add(hashmap_t *, ass_font_t *font); +void ass_font_cache_done(hashmap_t *); // Create definitions for bitmap_hash_key and glyph_hash_key #define CREATE_STRUCT_DEFINITIONS #include "ass_cache_template.c" typedef struct bitmap_hash_val_s { - bitmap_t* bm; // the actual bitmaps - bitmap_t* bm_o; - bitmap_t* bm_s; + bitmap_t *bm; // the actual bitmaps + bitmap_t *bm_o; + bitmap_t *bm_s; } bitmap_hash_val_t; -void ass_bitmap_cache_init(void); -void* cache_add_bitmap(bitmap_hash_key_t* key, bitmap_hash_val_t* val); -bitmap_hash_val_t* cache_find_bitmap(bitmap_hash_key_t* key); -void ass_bitmap_cache_reset(void); -void ass_bitmap_cache_done(void); +hashmap_t *ass_bitmap_cache_init(void); +void *cache_add_bitmap(hashmap_t *, bitmap_hash_key_t *key, + bitmap_hash_val_t *val); +bitmap_hash_val_t *cache_find_bitmap(hashmap_t *bitmap_cache, + bitmap_hash_key_t *key); +hashmap_t *ass_bitmap_cache_reset(hashmap_t *bitmap_cache); +void ass_bitmap_cache_done(hashmap_t *bitmap_cache); -// Cache for composited bitmaps -typedef struct composite_hash_key_s { - int aw, ah, bw, bh; - int ax, ay, bx, by; - bitmap_hash_key_t a; - bitmap_hash_key_t b; -} composite_hash_key_t; - typedef struct composite_hash_val_s { - unsigned char* a; - unsigned char* b; + unsigned char *a; + unsigned char *b; } composite_hash_val_t; -void ass_composite_cache_init(void); -void* cache_add_composite(composite_hash_key_t* key, composite_hash_val_t* val); -composite_hash_val_t* cache_find_composite(composite_hash_key_t* key); -void ass_composite_cache_reset(void); -void ass_composite_cache_done(void); +hashmap_t *ass_composite_cache_init(void); +void *cache_add_composite(hashmap_t *, composite_hash_key_t *key, + composite_hash_val_t *val); +composite_hash_val_t *cache_find_composite(hashmap_t *composite_cache, + composite_hash_key_t *key); +hashmap_t *ass_composite_cache_reset(hashmap_t *composite_cache); +void ass_composite_cache_done(hashmap_t *composite_cache); typedef struct glyph_hash_val_s { - FT_Glyph glyph; - FT_Glyph outline_glyph; - FT_BBox bbox_scaled; // bbox after scaling, but before rotation - FT_Vector advance; // 26.6, advance distance to the next bitmap in line + FT_Glyph glyph; + FT_Glyph outline_glyph; + FT_BBox bbox_scaled; // bbox after scaling, but before rotation + FT_Vector advance; // 26.6, advance distance to the next bitmap in line + int asc, desc; // ascender/descender of a drawing } glyph_hash_val_t; -void ass_glyph_cache_init(void); -void* cache_add_glyph(glyph_hash_key_t* key, glyph_hash_val_t* val); -glyph_hash_val_t* cache_find_glyph(glyph_hash_key_t* key); -void ass_glyph_cache_reset(void); -void ass_glyph_cache_done(void); +hashmap_t *ass_glyph_cache_init(void); +void *cache_add_glyph(hashmap_t *, glyph_hash_key_t *key, + glyph_hash_val_t *val); +glyph_hash_val_t *cache_find_glyph(hashmap_t *glyph_cache, + glyph_hash_key_t *key); +hashmap_t *ass_glyph_cache_reset(hashmap_t *glyph_cache); +void ass_glyph_cache_done(hashmap_t *glyph_cache); -typedef struct hashmap_s hashmap_t; -typedef void (*hashmap_item_dtor_t)(void* key, size_t key_size, void* value, size_t value_size); -typedef int (*hashmap_key_compare_t)(void* key1, void* key2, size_t key_size); -typedef unsigned (*hashmap_hash_t)(void* key, size_t key_size); - -hashmap_t* hashmap_init(size_t key_size, size_t value_size, int nbuckets, - hashmap_item_dtor_t item_dtor, hashmap_key_compare_t key_compare, - hashmap_hash_t hash); -void hashmap_done(hashmap_t* map); -void* hashmap_insert(hashmap_t* map, void* key, void* value); -void* hashmap_find(hashmap_t* map, void* key); - -#endif /* LIBASS_CACHE_H */ +#endif /* LIBASS_CACHE_H */ diff --git a/aegisub/libass/ass_cache_template.c b/aegisub/libass/ass_cache_template.c index 7f9ec95f5..eae980747 100644 --- a/aegisub/libass/ass_cache_template.c +++ b/aegisub/libass/ass_cache_template.c @@ -6,6 +6,8 @@ type member; #define FTVECTOR(member) \ FT_Vector member; +#define BITMAPHASHKEY(member) \ + bitmap_hash_key_t member; #define END(typedefnamename) \ } typedefnamename; @@ -21,6 +23,8 @@ a->member == b->member && #define FTVECTOR(member) \ a->member.x == b->member.x && a->member.y == b->member.y && +#define BITMAPHASHKEY(member) \ + bitmap_compare(&a->member, &b->member, sizeof(a->member)) && #define END(typedefname) \ 1; \ } @@ -35,6 +39,10 @@ #define GENERIC(type, member) \ hval = fnv_32a_buf(&p->member, sizeof(p->member), hval); #define FTVECTOR(member) GENERIC(, member.x); GENERIC(, member.y); +#define BITMAPHASHKEY(member) { \ + unsigned temp = bitmap_hash(&p->member, sizeof(p->member)); \ + hval = fnv_32a_buf(&temp, sizeof(temp), hval); \ + } #define END(typedefname) \ return hval; \ } @@ -51,7 +59,7 @@ START(bitmap, bipmap_hash_key_s) GENERIC(ass_font_t *, font) GENERIC(double, size) // font size GENERIC(uint32_t, ch) // character code - GENERIC(unsigned, outline) // border width, 16.16 fixed point value + FTVECTOR(outline) // border width, 16.16 fixed point value GENERIC(int, bold) GENERIC(int, italic) GENERIC(char, be) // blur edges @@ -61,12 +69,16 @@ START(bitmap, bipmap_hash_key_s) GENERIC(int, frx) // signed 16.16 GENERIC(int, fry) // signed 16.16 GENERIC(int, frz) // signed 16.16 + GENERIC(int, fax) // signed 16.16 + GENERIC(int, fay) // signed 16.16 // shift vector that was added to glyph before applying rotation // = 0, if frx = fry = frx = 0 // = (glyph base point) - (rotation origin), otherwise GENERIC(int, shift_x) GENERIC(int, shift_y) FTVECTOR(advance) // subpixel shift vector + FTVECTOR(shadow_offset) // shadow subpixel shift + GENERIC(unsigned, drawing_hash) // hashcode of a drawing END(bitmap_hash_key_t) // describes an outline glyph @@ -79,10 +91,28 @@ START(glyph, glyph_hash_key_s) GENERIC(unsigned, scale_x) // 16.16 GENERIC(unsigned, scale_y) // 16.16 FTVECTOR(advance) // subpixel shift vector - GENERIC(unsigned, outline) // border width, 16.16 + FTVECTOR(outline) // border width, 16.16 + GENERIC(unsigned, drawing_hash) // hashcode of a drawing + GENERIC(unsigned, flags) // glyph decoration flags END(glyph_hash_key_t) +// Cache for composited bitmaps +START(composite, composite_hash_key_s) + GENERIC(int, aw) + GENERIC(int, ah) + GENERIC(int, bw) + GENERIC(int, bh) + GENERIC(int, ax) + GENERIC(int, ay) + GENERIC(int, bx) + GENERIC(int, by) + BITMAPHASHKEY(a) + BITMAPHASHKEY(b) +END(composite_hash_key_t) + + #undef START #undef GENERIC #undef FTVECTOR +#undef BITMAPHASHKEY #undef END diff --git a/aegisub/libass/ass_drawing.c b/aegisub/libass/ass_drawing.c new file mode 100644 index 000000000..09cf2d004 --- /dev/null +++ b/aegisub/libass/ass_drawing.c @@ -0,0 +1,477 @@ +/* + * Copyright (C) 2009 Grigori Goronzy + * + * This file is part of libass. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include +#include FT_GLYPH_H +#include FT_OUTLINE_H +#include FT_BBOX_H +#include + +#include "ass_utils.h" +#include "ass_font.h" +#include "ass_drawing.h" + +#define CURVE_ACCURACY 64.0 +#define GLYPH_INITIAL_POINTS 100 +#define GLYPH_INITIAL_CONTOURS 5 + +/* + * \brief Get and prepare a FreeType glyph + */ +static void drawing_make_glyph(ass_drawing_t *drawing, void *fontconfig_priv, + ass_font_t *font, ass_hinting_t hint) +{ + FT_OutlineGlyph glyph; + + // This is hacky... + glyph = (FT_OutlineGlyph) ass_font_get_glyph(fontconfig_priv, font, + (uint32_t) ' ', hint, 0); + + FT_Outline_Done(drawing->ftlibrary, &glyph->outline); + FT_Outline_New(drawing->ftlibrary, GLYPH_INITIAL_POINTS, + GLYPH_INITIAL_CONTOURS, &glyph->outline); + + glyph->outline.n_contours = 0; + glyph->outline.n_points = 0; + glyph->root.advance.x = glyph->root.advance.y = 0; + drawing->glyph = glyph; +} + +/* + * \brief Add a single point to a contour. + */ +static inline void drawing_add_point(ass_drawing_t *drawing, + FT_Vector *point) +{ + FT_Outline *ol = &drawing->glyph->outline; + + if (ol->n_points >= drawing->max_points) { + drawing->max_points *= 2; + ol->points = realloc(ol->points, sizeof(FT_Vector) * + drawing->max_points); + ol->tags = realloc(ol->tags, drawing->max_points); + } + + ol->points[ol->n_points].x = point->x; + ol->points[ol->n_points].y = point->y; + ol->tags[ol->n_points] = 1; + ol->n_points++; +} + +/* + * \brief Close a contour and check glyph size overflow. + */ +static inline void drawing_close_shape(ass_drawing_t *drawing) +{ + FT_Outline *ol = &drawing->glyph->outline; + + if (ol->n_contours >= drawing->max_contours) { + drawing->max_contours *= 2; + ol->contours = realloc(ol->contours, sizeof(short) * + drawing->max_contours); + } + + ol->contours[ol->n_contours] = ol->n_points - 1; + ol->n_contours++; +} + +/* + * \brief Prepare drawing for parsing. This just sets a few parameters. + */ +static void drawing_prepare(ass_drawing_t *drawing) +{ + // Scaling parameters + drawing->point_scale_x = drawing->scale_x * + 64.0 / (1 << (drawing->scale - 1)); + drawing->point_scale_y = drawing->scale_y * + 64.0 / (1 << (drawing->scale - 1)); +} + +/* + * \brief Finish a drawing. This only sets the horizontal advance according + * to the glyph's bbox at the moment. + */ +static void drawing_finish(ass_drawing_t *drawing) +{ + int i, offset; + FT_BBox bbox; + FT_Outline *ol = &drawing->glyph->outline; + + // Close the last contour + drawing_close_shape(drawing); + +#if 0 + // Dump points + for (i = 0; i < ol->n_points; i++) { + printf("point (%d, %d)\n", (int) ol->points[i].x, + (int) ol->points[i].y); + } + + // Dump contours + for (i = 0; i < ol->n_contours; i++) + printf("contour %d\n", ol->contours[i]); +#endif + + FT_Outline_Get_CBox(&drawing->glyph->outline, &bbox); + drawing->glyph->root.advance.x = d6_to_d16(bbox.xMax - bbox.xMin); + + drawing->desc = double_to_d6(-drawing->pbo * drawing->scale_y); + drawing->asc = bbox.yMax - bbox.yMin + drawing->desc; + + // Place it onto the baseline + offset = (bbox.yMax - bbox.yMin) + double_to_d6(-drawing->pbo * + drawing->scale_y); + for (i = 0; i < ol->n_points; i++) + ol->points[i].y += offset; +} + +/* + * \brief Check whether a number of items on the list is available + */ +static int token_check_values(ass_drawing_token_t *token, int i, int type) +{ + int j; + for (j = 0; j < i; j++) { + if (!token || token->type != type) return 0; + token = token->next; + } + + return 1; +} + +/* + * \brief Tokenize a drawing string into a list of ass_drawing_token_t + * This also expands points for closing b-splines + */ +static ass_drawing_token_t *drawing_tokenize(char *str) +{ + char *p = str; + int i, val, type = -1, is_set = 0; + FT_Vector point = {0, 0}; + + ass_drawing_token_t *root = NULL, *tail = NULL, *spline_start = NULL; + + while (*p) { + if (*p == 'c' && spline_start) { + // Close b-splines: add the first three points of the b-spline + // back to the end + if (token_check_values(spline_start->next, 2, TOKEN_B_SPLINE)) { + for (i = 0; i < 3; i++) { + tail->next = calloc(1, sizeof(ass_drawing_token_t)); + tail->next->prev = tail; + tail = tail->next; + tail->type = TOKEN_B_SPLINE; + tail->point = spline_start->point; + spline_start = spline_start->next; + } + spline_start = NULL; + } + } else if (!is_set && mystrtoi(&p, &val)) { + point.x = val; + is_set = 1; + p--; + } else if (is_set == 1 && mystrtoi(&p, &val)) { + point.y = val; + is_set = 2; + p--; + } else if (*p == 'm') + type = TOKEN_MOVE; + else if (*p == 'n') + type = TOKEN_MOVE_NC; + else if (*p == 'l') + type = TOKEN_LINE; + else if (*p == 'b') + type = TOKEN_CUBIC_BEZIER; + else if (*p == 'q') + type = TOKEN_CONIC_BEZIER; + else if (*p == 's') + type = TOKEN_B_SPLINE; + // We're simply ignoring TOKEN_EXTEND_B_SPLINE here. + // This is not harmful at all, since it can be ommitted with + // similar result (the spline is extended anyway). + + if (type != -1 && is_set == 2) { + if (root) { + tail->next = calloc(1, sizeof(ass_drawing_token_t)); + tail->next->prev = tail; + tail = tail->next; + } else + root = tail = calloc(1, sizeof(ass_drawing_token_t)); + tail->type = type; + tail->point = point; + is_set = 0; + if (type == TOKEN_B_SPLINE && !spline_start) + spline_start = tail->prev; + } + p++; + } + +#if 0 + // Check tokens + ass_drawing_token_t *t = root; + while(t) { + printf("token %d point (%d, %d)\n", t->type, t->point.x, t->point.y); + t = t->next; + } +#endif + + return root; +} + +/* + * \brief Free a list of tokens + */ +static void drawing_free_tokens(ass_drawing_token_t *token) +{ + while (token) { + ass_drawing_token_t *at = token; + token = token->next; + free(at); + } +} + +/* + * \brief Translate and scale a point coordinate according to baseline + * offset and scale. + */ +static inline void translate_point(ass_drawing_t *drawing, FT_Vector *point) +{ + point->x = drawing->point_scale_x * point->x; + point->y = drawing->point_scale_y * -point->y; +} + +/* + * \brief Evaluate a curve into lines + * This curve evaluator is also used in VSFilter (RTS.cpp); it's a simple + * implementation of the De Casteljau algorithm. + */ +static void drawing_evaluate_curve(ass_drawing_t *drawing, + ass_drawing_token_t *token, char spline, + int started) +{ + double cx3, cx2, cx1, cx0, cy3, cy2, cy1, cy0; + double t, h, max_accel, max_accel1, max_accel2; + FT_Vector cur = {0, 0}; + + cur = token->point; + translate_point(drawing, &cur); + int x0 = cur.x; + int y0 = cur.y; + token = token->next; + cur = token->point; + translate_point(drawing, &cur); + int x1 = cur.x; + int y1 = cur.y; + token = token->next; + cur = token->point; + translate_point(drawing, &cur); + int x2 = cur.x; + int y2 = cur.y; + token = token->next; + cur = token->point; + translate_point(drawing, &cur); + int x3 = cur.x; + int y3 = cur.y; + + if (spline) { + // 1 [-1 +3 -3 +1] + // - * [+3 -6 +3 0] + // 6 [-3 0 +3 0] + // [+1 +4 +1 0] + + double div6 = 1.0/6.0; + + cx3 = div6*(- x0+3*x1-3*x2+x3); + cx2 = div6*( 3*x0-6*x1+3*x2); + cx1 = div6*(-3*x0 +3*x2); + cx0 = div6*( x0+4*x1+1*x2); + + cy3 = div6*(- y0+3*y1-3*y2+y3); + cy2 = div6*( 3*y0-6*y1+3*y2); + cy1 = div6*(-3*y0 +3*y2); + cy0 = div6*( y0+4*y1+1*y2); + } else { + // [-1 +3 -3 +1] + // [+3 -6 +3 0] + // [-3 +3 0 0] + // [+1 0 0 0] + + cx3 = - x0+3*x1-3*x2+x3; + cx2 = 3*x0-6*x1+3*x2; + cx1 = -3*x0+3*x1; + cx0 = x0; + + cy3 = - y0+3*y1-3*y2+y3; + cy2 = 3*y0-6*y1+3*y2; + cy1 = -3*y0+3*y1; + cy0 = y0; + } + + max_accel1 = fabs(2 * cy2) + fabs(6 * cy3); + max_accel2 = fabs(2 * cx2) + fabs(6 * cx3); + + max_accel = FFMAX(max_accel1, max_accel2); + h = 1.0; + + if (max_accel > CURVE_ACCURACY) + h = sqrt(CURVE_ACCURACY / max_accel); + + if (!started) { + cur.x = cx0; + cur.y = cy0; + drawing_add_point(drawing, &cur); + } + + for (t = 0; t < 1.0; t += h) { + cur.x = cx0 + t * (cx1 + t * (cx2 + t * cx3)); + cur.y = cy0 + t * (cy1 + t * (cy2 + t * cy3)); + drawing_add_point(drawing, &cur); + } + + cur.x = cx0 + cx1 + cx2 + cx3; + cur.y = cy0 + cy1 + cy2 + cy3; + drawing_add_point(drawing, &cur); +} + +/* + * \brief Create and initialize a new drawing and return it + */ +ass_drawing_t *ass_drawing_new(void *fontconfig_priv, ass_font_t *font, + ass_hinting_t hint, FT_Library lib) +{ + ass_drawing_t* drawing; + + drawing = calloc(1, sizeof(*drawing)); + drawing->text = malloc(DRAWING_INITIAL_SIZE); + drawing->size = DRAWING_INITIAL_SIZE; + + drawing->ftlibrary = lib; + drawing_make_glyph(drawing, fontconfig_priv, font, hint); + + drawing->scale_x = 1.; + drawing->scale_y = 1.; + drawing->max_contours = GLYPH_INITIAL_CONTOURS; + drawing->max_points = GLYPH_INITIAL_POINTS; + + return drawing; +} + +/* + * \brief Free a drawing + */ +void ass_drawing_free(ass_drawing_t* drawing) +{ + free(drawing->text); + free(drawing); +} + +/* + * \brief Add one ASCII character to the drawing text buffer + */ +void ass_drawing_add_char(ass_drawing_t* drawing, char symbol) +{ + drawing->text[drawing->i++] = symbol; + drawing->text[drawing->i] = 0; + + if (drawing->i + 1 >= drawing->size) { + drawing->size *= 2; + drawing->text = realloc(drawing->text, drawing->size); + } +} + +/* + * \brief Create a hashcode for the drawing + * XXX: To avoid collisions a better hash algorithm might be useful. + */ +void ass_drawing_hash(ass_drawing_t* drawing) +{ + drawing->hash = fnv_32a_str(drawing->text, FNV1_32A_INIT); +} + +/* + * \brief Convert token list to outline. Calls the line and curve evaluators. + */ +FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing) +{ + int started = 0; + ass_drawing_token_t *token; + FT_Vector pen = {0, 0}; + + drawing->tokens = drawing_tokenize(drawing->text); + drawing_prepare(drawing); + + token = drawing->tokens; + while (token) { + // Draw something according to current command + switch (token->type) { + case TOKEN_MOVE_NC: + pen = token->point; + translate_point(drawing, &pen); + token = token->next; + break; + case TOKEN_MOVE: + pen = token->point; + translate_point(drawing, &pen); + if (started) { + drawing_close_shape(drawing); + started = 0; + } + token = token->next; + break; + case TOKEN_LINE: { + FT_Vector to; + to = token->point; + translate_point(drawing, &to); + if (!started) drawing_add_point(drawing, &pen); + drawing_add_point(drawing, &to); + started = 1; + token = token->next; + break; + } + case TOKEN_CUBIC_BEZIER: + if (token_check_values(token, 3, TOKEN_CUBIC_BEZIER) && + token->prev) { + drawing_evaluate_curve(drawing, token->prev, 0, started); + token = token->next; + token = token->next; + token = token->next; + started = 1; + } else + token = token->next; + break; + case TOKEN_B_SPLINE: + if (token_check_values(token, 3, TOKEN_B_SPLINE) && + token->prev) { + drawing_evaluate_curve(drawing, token->prev, 1, started); + token = token->next; + started = 1; + } else + token = token->next; + break; + default: + token = token->next; + break; + } + } + + drawing_finish(drawing); + drawing_free_tokens(drawing->tokens); + return &drawing->glyph; +} + + diff --git a/aegisub/libass/ass_drawing.h b/aegisub/libass/ass_drawing.h new file mode 100644 index 000000000..323c05dee --- /dev/null +++ b/aegisub/libass/ass_drawing.h @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2009 Grigori Goronzy + * + * This file is part of libass. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef LIBASS_DRAWING_H +#define LIBASS_DRAWING_H + +#include +#include FT_GLYPH_H + +#include "ass.h" + +#define DRAWING_INITIAL_SIZE 256 + +enum ass_token_type { + TOKEN_MOVE, + TOKEN_MOVE_NC, + TOKEN_LINE, + TOKEN_CUBIC_BEZIER, + TOKEN_CONIC_BEZIER, + TOKEN_B_SPLINE, + TOKEN_EXTEND_SPLINE, + TOKEN_CLOSE +}; + +typedef struct ass_drawing_token_s { + enum ass_token_type type; + FT_Vector point; + struct ass_drawing_token_s *next; + struct ass_drawing_token_s *prev; +} ass_drawing_token_t; + +typedef struct ass_drawing_s { + char *text; // drawing string + int i; // text index + int scale; // scale (1-64) for subpixel accuracy + double pbo; // drawing will be shifted in y direction by this amount + double scale_x; // FontScaleX + double scale_y; // FontScaleY + int asc; // ascender + int desc; // descender + FT_OutlineGlyph glyph; // the "fake" glyph created for later rendering + int hash; // hash value (for caching) + + // private + FT_Library ftlibrary; // FT library instance, needed for font ops + int size; // current buffer size + ass_drawing_token_t *tokens; // tokenized drawing + int max_points; // current maximum size + int max_contours; + double point_scale_x; + double point_scale_y; +} ass_drawing_t; + +ass_drawing_t *ass_drawing_new(void *fontconfig_priv, ass_font_t *font, + ass_hinting_t hint, FT_Library lib); +void ass_drawing_free(ass_drawing_t* drawing); +void ass_drawing_add_char(ass_drawing_t* drawing, char symbol); +void ass_drawing_hash(ass_drawing_t* drawing); +FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing); + +#endif /* LIBASS_DRAWING_H */ diff --git a/aegisub/libass/ass_font.c b/aegisub/libass/ass_font.c index 579e658f1..0aeeec6c1 100644 --- a/aegisub/libass/ass_font.c +++ b/aegisub/libass/ass_font.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 * @@ -36,7 +34,6 @@ #include "ass_cache.h" #include "ass_fontconfig.h" #include "ass_utils.h" -#include "mputils.h" /** * Select Microfost Unicode CharMap, if the font has one. @@ -44,200 +41,218 @@ */ static void charmap_magic(FT_Face face) { - int i; - for (i = 0; i < face->num_charmaps; ++i) { - FT_CharMap cmap = face->charmaps[i]; - unsigned pid = cmap->platform_id; - unsigned eid = cmap->encoding_id; - if (pid == 3 /*microsoft*/ && (eid == 1 /*unicode bmp*/ || eid == 10 /*full unicode*/)) { - FT_Set_Charmap(face, cmap); - return; - } - } + int i; + for (i = 0; i < face->num_charmaps; ++i) { + FT_CharMap cmap = face->charmaps[i]; + unsigned pid = cmap->platform_id; + unsigned eid = cmap->encoding_id; + if (pid == 3 /*microsoft */ + && (eid == 1 /*unicode bmp */ + || eid == 10 /*full unicode */ )) { + FT_Set_Charmap(face, cmap); + return; + } + } - if (!face->charmap) { - if (face->num_charmaps == 0) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NoCharmaps); - return; - } - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NoCharmapAutodetected); - FT_Set_Charmap(face, face->charmaps[0]); - return; - } + if (!face->charmap) { + if (face->num_charmaps == 0) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_NoCharmaps); + return; + } + ass_msg(MSGL_WARN, MSGTR_LIBASS_NoCharmapAutodetected); + FT_Set_Charmap(face, face->charmaps[0]); + return; + } } -static void update_transform(ass_font_t* font) +static void update_transform(ass_font_t *font) { - int i; - FT_Matrix m; - m.xx = double_to_d16(font->scale_x); - m.yy = double_to_d16(font->scale_y); - m.xy = m.yx = 0; - for (i = 0; i < font->n_faces; ++i) - FT_Set_Transform(font->faces[i], &m, &font->v); + int i; + FT_Matrix m; + m.xx = double_to_d16(font->scale_x); + m.yy = double_to_d16(font->scale_y); + m.xy = m.yx = 0; + for (i = 0; i < font->n_faces; ++i) + FT_Set_Transform(font->faces[i], &m, &font->v); } /** * \brief find a memory font by name */ -static int find_font(ass_library_t* library, char* name) +static int find_font(ass_library_t *library, char *name) { - int i; - for (i = 0; i < library->num_fontdata; ++i) - if (strcasecmp(name, library->fontdata[i].name) == 0) - return i; - return -1; + int i; + for (i = 0; i < library->num_fontdata; ++i) + if (strcasecmp(name, library->fontdata[i].name) == 0) + return i; + return -1; } static void face_set_size(FT_Face face, double size); static void buggy_font_workaround(FT_Face face) { - // Some fonts have zero Ascender/Descender fields in 'hhea' table. - // In this case, get the information from 'os2' table or, as - // a last resort, from face.bbox. - if (face->ascender + face->descender == 0 || face->height == 0) { - TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); - if (os2) { - face->ascender = os2->sTypoAscender; - face->descender = os2->sTypoDescender; - face->height = face->ascender - face->descender; - } else { - face->ascender = face->bbox.yMax; - face->descender = face->bbox.yMin; - face->height = face->ascender - face->descender; - } - } + // Some fonts have zero Ascender/Descender fields in 'hhea' table. + // In this case, get the information from 'os2' table or, as + // a last resort, from face.bbox. + if (face->ascender + face->descender == 0 || face->height == 0) { + TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); + if (os2) { + face->ascender = os2->sTypoAscender; + face->descender = os2->sTypoDescender; + face->height = face->ascender - face->descender; + } else { + face->ascender = face->bbox.yMax; + face->descender = face->bbox.yMin; + face->height = face->ascender - face->descender; + } + } } /** * \brief Select a face with the given charcode and add it to ass_font_t * \return index of the new face in font->faces, -1 if failed */ -static int add_face(void* fc_priv, ass_font_t* font, uint32_t ch) +static int add_face(void *fc_priv, ass_font_t *font, uint32_t ch) { - char* path; - int index; - FT_Face face; - int error; - int mem_idx; - - if (font->n_faces == ASS_FONT_MAX_FACES) - return -1; - - path = fontconfig_select(fc_priv, font->desc.family, font->desc.treat_family_as_pattern, font->desc.bold, - font->desc.italic, &index, ch); + char *path; + int index; + FT_Face face; + int error; + int mem_idx; - mem_idx = find_font(font->library, path); - if (mem_idx >= 0) { - error = FT_New_Memory_Face(font->ftlibrary, (unsigned char*)font->library->fontdata[mem_idx].data, - font->library->fontdata[mem_idx].size, 0, &face); - if (error) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorOpeningMemoryFont, path); - return -1; - } - } else { - error = FT_New_Face(font->ftlibrary, path, index, &face); - if (error) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorOpeningFont, path, index); - return -1; - } - } - charmap_magic(face); - buggy_font_workaround(face); - - font->faces[font->n_faces++] = face; - update_transform(font); - face_set_size(face, font->size); - return font->n_faces - 1; + if (font->n_faces == ASS_FONT_MAX_FACES) + return -1; + + path = + fontconfig_select(fc_priv, font->desc.family, + font->desc.treat_family_as_pattern, + font->desc.bold, font->desc.italic, &index, ch); + if (!path) + return -1; + + mem_idx = find_font(font->library, path); + if (mem_idx >= 0) { + error = + FT_New_Memory_Face(font->ftlibrary, + (unsigned char *) font->library-> + fontdata[mem_idx].data, + font->library->fontdata[mem_idx].size, 0, + &face); + if (error) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_ErrorOpeningMemoryFont, + path); + free(path); + return -1; + } + } else { + error = FT_New_Face(font->ftlibrary, path, index, &face); + if (error) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_ErrorOpeningFont, path, + index); + free(path); + return -1; + } + } + charmap_magic(face); + buggy_font_workaround(face); + + font->faces[font->n_faces++] = face; + update_transform(font); + face_set_size(face, font->size); + free(path); + return font->n_faces - 1; } /** * \brief Create a new ass_font_t according to "desc" argument */ -ass_font_t* ass_font_new(ass_library_t* library, FT_Library ftlibrary, void* fc_priv, ass_font_desc_t* desc) +ass_font_t *ass_font_new(void *font_cache, ass_library_t *library, + FT_Library ftlibrary, void *fc_priv, + ass_font_desc_t *desc) { - int error; - ass_font_t* fontp; - ass_font_t font; + int error; + ass_font_t *fontp; + ass_font_t font; - fontp = ass_font_cache_find(desc); - if (fontp) - return fontp; - - font.library = library; - font.ftlibrary = ftlibrary; - font.n_faces = 0; - font.desc.family = strdup(desc->family); - font.desc.treat_family_as_pattern = desc->treat_family_as_pattern; - font.desc.bold = desc->bold; - font.desc.italic = desc->italic; + fontp = ass_font_cache_find((hashmap_t *) font_cache, desc); + if (fontp) + return fontp; - font.scale_x = font.scale_y = 1.; - font.v.x = font.v.y = 0; - font.size = 0.; + font.library = library; + font.ftlibrary = ftlibrary; + font.n_faces = 0; + font.desc.family = strdup(desc->family); + font.desc.treat_family_as_pattern = desc->treat_family_as_pattern; + font.desc.bold = desc->bold; + font.desc.italic = desc->italic; - error = add_face(fc_priv, &font, 0); - if (error == -1) { - free(font.desc.family); - return 0; - } else - return ass_font_cache_add(&font); + font.scale_x = font.scale_y = 1.; + font.v.x = font.v.y = 0; + font.size = 0.; + + error = add_face(fc_priv, &font, 0); + if (error == -1) { + free(font.desc.family); + return 0; + } else + return ass_font_cache_add((hashmap_t *) font_cache, &font); } /** * \brief Set font transformation matrix and shift vector **/ -void ass_font_set_transform(ass_font_t* font, double scale_x, double scale_y, FT_Vector* v) +void ass_font_set_transform(ass_font_t *font, double scale_x, + double scale_y, FT_Vector *v) { - font->scale_x = scale_x; - font->scale_y = scale_y; - font->v.x = v->x; - font->v.y = v->y; - update_transform(font); + font->scale_x = scale_x; + font->scale_y = scale_y; + font->v.x = v->x; + font->v.y = v->y; + update_transform(font); } static void face_set_size(FT_Face face, double size) { #if (FREETYPE_MAJOR > 2) || ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR > 1)) - TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea); - TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); - double mscale = 1.; - FT_Size_RequestRec rq; - FT_Size_Metrics *m = &face->size->metrics; - // VSFilter uses metrics from TrueType OS/2 table - // The idea was borrowed from asa (http://asa.diac24.net) - if (hori && os2) { - int hori_height = hori->Ascender - hori->Descender; - int os2_height = os2->usWinAscent + os2->usWinDescent; - if (hori_height && os2_height) - mscale = (double)hori_height / os2_height; - } - memset(&rq, 0, sizeof(rq)); - rq.type = FT_SIZE_REQUEST_TYPE_REAL_DIM; - rq.width = 0; - rq.height = double_to_d6(size * mscale); - rq.horiResolution = rq.vertResolution = 0; - FT_Request_Size(face, &rq); - m->ascender /= mscale; - m->descender /= mscale; - m->height /= mscale; + TT_HoriHeader *hori = FT_Get_Sfnt_Table(face, ft_sfnt_hhea); + TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); + double mscale = 1.; + FT_Size_RequestRec rq; + FT_Size_Metrics *m = &face->size->metrics; + // VSFilter uses metrics from TrueType OS/2 table + // The idea was borrowed from asa (http://asa.diac24.net) + if (hori && os2) { + int hori_height = hori->Ascender - hori->Descender; + int os2_height = os2->usWinAscent + os2->usWinDescent; + if (hori_height && os2_height) + mscale = (double) hori_height / os2_height; + } + memset(&rq, 0, sizeof(rq)); + rq.type = FT_SIZE_REQUEST_TYPE_REAL_DIM; + rq.width = 0; + rq.height = double_to_d6(size * mscale); + rq.horiResolution = rq.vertResolution = 0; + FT_Request_Size(face, &rq); + m->ascender /= mscale; + m->descender /= mscale; + m->height /= mscale; #else - FT_Set_Char_Size(face, 0, double_to_d6(size), 0, 0); + FT_Set_Char_Size(face, 0, double_to_d6(size), 0, 0); #endif } /** * \brief Set font size **/ -void ass_font_set_size(ass_font_t* font, double size) +void ass_font_set_size(ass_font_t *font, double size) { - int i; - if (font->size != size) { - font->size = size; - for (i = 0; i < font->n_faces; ++i) - face_set_size(font->faces[i], size); - } + int i; + if (font->size != size) { + font->size = size; + for (i = 0; i < font->n_faces; ++i) + face_set_size(font->faces[i], size); + } } /** @@ -245,125 +260,222 @@ void ass_font_set_size(ass_font_t* font, double size) * \param ch character code * The values are extracted from the font face that provides glyphs for the given character **/ -void ass_font_get_asc_desc(ass_font_t* font, uint32_t ch, int* asc, int* desc) +void ass_font_get_asc_desc(ass_font_t *font, uint32_t ch, int *asc, + int *desc) { - int i; - for (i = 0; i < font->n_faces; ++i) { - FT_Face face = font->faces[i]; - if (FT_Get_Char_Index(face, ch)) { - *asc = face->size->metrics.ascender; - *desc = - face->size->metrics.descender; - return; - } - } - - *asc = *desc = 0; + int i; + for (i = 0; i < font->n_faces; ++i) { + FT_Face face = font->faces[i]; + if (FT_Get_Char_Index(face, ch)) { + *asc = face->size->metrics.ascender; + *desc = -face->size->metrics.descender; + return; + } + } + + *asc = *desc = 0; +} + +/* + * Strike a glyph with a horizontal line; it's possible to underline it + * and/or strike through it. For the line's position and size, truetype + * tables are consulted. Obviously this relies on the data in the tables + * being accurate. + * + */ +static int ass_strike_outline_glyph(FT_Face face, ass_font_t *font, + FT_Glyph glyph, int under, int through) +{ + TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); + TT_Postscript *ps = FT_Get_Sfnt_Table(face, ft_sfnt_post); + FT_Outline *ol = &((FT_OutlineGlyph) glyph)->outline; + int bear, advance, y_scale, i; + + // Grow outline + i = (under ? 4 : 0) + (through ? 4 : 0); + ol->points = realloc(ol->points, sizeof(FT_Vector) * + (ol->n_points + i)); + ol->tags = realloc(ol->tags, ol->n_points + i); + i = !!under + !!through; + ol->contours = realloc(ol->contours, sizeof(short) * + (ol->n_contours + i)); + + // If the bearing is negative, the glyph starts left of the current + // pen position + bear = FFMIN(face->glyph->metrics.horiBearingX, 0); + // We're adding half a pixel to avoid small gaps + advance = d16_to_d6(glyph->advance.x) + 32; + y_scale = face->size->metrics.y_scale; + + // Add points to the outline + if (under) { + int pos, size; + pos = FT_MulFix(ps->underlinePosition, y_scale * font->scale_y); + size = FT_MulFix(ps->underlineThickness, + y_scale * font->scale_y / 2); + + if (pos > 0 || size <= 0) + return 0; + + FT_Vector points[4] = { + {.x = bear, .y = pos + size}, + {.x = advance, .y = pos + size}, + {.x = advance, .y = pos - size}, + {.x = bear, .y = pos - size}, + }; + + for (i = 0; i < 4; i++) { + ol->points[ol->n_points] = points[i]; + ol->tags[ol->n_points++] = 1; + } + ol->contours[ol->n_contours++] = ol->n_points - 1; + } + + if (through) { + int pos, size; + pos = FT_MulFix(os2->yStrikeoutPosition, y_scale * font->scale_y); + size = FT_MulFix(os2->yStrikeoutSize, y_scale * font->scale_y / 2); + + if (pos < 0 || size <= 0) + return 0; + + FT_Vector points[4] = { + {.x = bear, .y = pos + size}, + {.x = advance, .y = pos + size}, + {.x = advance, .y = pos - size}, + {.x = bear, .y = pos - size}, + }; + + for (i = 0; i < 4; i++) { + ol->points[ol->n_points] = points[i]; + ol->tags[ol->n_points++] = 1; + } + + ol->contours[ol->n_contours++] = ol->n_points - 1; + } + + return 1; } /** * \brief Get a glyph * \param ch character code **/ -FT_Glyph ass_font_get_glyph(void* fontconfig_priv, ass_font_t* font, uint32_t ch, ass_hinting_t hinting) +FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ass_font_t *font, + uint32_t ch, ass_hinting_t hinting, int deco) { - int error; - int index = 0; - int i; - FT_Glyph glyph; - FT_Face face = 0; - int flags = 0; + int error; + int index = 0; + int i; + FT_Glyph glyph; + FT_Face face = 0; + int flags = 0; - if (ch < 0x20) - return 0; - if (font->n_faces == 0) - return 0; + if (ch < 0x20) + return 0; + if (font->n_faces == 0) + return 0; - for (i = 0; i < font->n_faces; ++i) { - face = font->faces[i]; - index = FT_Get_Char_Index(face, ch); - if (index) - break; - } + for (i = 0; i < font->n_faces; ++i) { + face = font->faces[i]; + index = FT_Get_Char_Index(face, ch); + if (index) + break; + } #ifdef CONFIG_FONTCONFIG - if (index == 0) { - int face_idx; - mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_GlyphNotFoundReselectingFont, - ch, font->desc.family, font->desc.bold, font->desc.italic); - face_idx = add_face(fontconfig_priv, font, ch); - if (face_idx >= 0) { - face = font->faces[face_idx]; - index = FT_Get_Char_Index(face, ch); - if (index == 0) { - mp_msg(MSGT_ASS, MSGL_ERR, MSGTR_LIBASS_GlyphNotFound, - ch, font->desc.family, font->desc.bold, font->desc.italic); - } - } - } + if (index == 0) { + int face_idx; + ass_msg(MSGL_INFO, + MSGTR_LIBASS_GlyphNotFoundReselectingFont, ch, + font->desc.family, font->desc.bold, font->desc.italic); + face_idx = add_face(fontconfig_priv, font, ch); + if (face_idx >= 0) { + face = font->faces[face_idx]; + index = FT_Get_Char_Index(face, ch); + if (index == 0) { + ass_msg(MSGL_ERR, MSGTR_LIBASS_GlyphNotFound, + ch, font->desc.family, font->desc.bold, + font->desc.italic); + } + } + } #endif - switch (hinting) { - case ASS_HINTING_NONE: flags = FT_LOAD_NO_HINTING; break; - case ASS_HINTING_LIGHT: flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT; break; - case ASS_HINTING_NORMAL: flags = FT_LOAD_FORCE_AUTOHINT; break; - case ASS_HINTING_NATIVE: flags = 0; break; - } - - error = FT_Load_Glyph(face, index, FT_LOAD_NO_BITMAP | flags); - if (error) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorLoadingGlyph); - return 0; - } - + switch (hinting) { + case ASS_HINTING_NONE: + flags = FT_LOAD_NO_HINTING; + break; + case ASS_HINTING_LIGHT: + flags = FT_LOAD_FORCE_AUTOHINT | FT_LOAD_TARGET_LIGHT; + break; + case ASS_HINTING_NORMAL: + flags = FT_LOAD_FORCE_AUTOHINT; + break; + case ASS_HINTING_NATIVE: + flags = 0; + break; + } + + error = FT_Load_Glyph(face, index, FT_LOAD_NO_BITMAP | flags); + if (error) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_ErrorLoadingGlyph); + return 0; + } #if (FREETYPE_MAJOR > 2) || \ ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR >= 2)) || \ ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR == 1) && (FREETYPE_PATCH >= 10)) // FreeType >= 2.1.10 required - if (!(face->style_flags & FT_STYLE_FLAG_ITALIC) && - (font->desc.italic > 55)) { - FT_GlyphSlot_Oblique(face->glyph); - } + if (!(face->style_flags & FT_STYLE_FLAG_ITALIC) && + (font->desc.italic > 55)) { + FT_GlyphSlot_Oblique(face->glyph); + } #endif - error = FT_Get_Glyph(face->glyph, &glyph); - if (error) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorLoadingGlyph); - return 0; - } - - return glyph; + error = FT_Get_Glyph(face->glyph, &glyph); + if (error) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_ErrorLoadingGlyph); + return 0; + } + + ass_strike_outline_glyph(face, font, glyph, deco & DECO_UNDERLINE, + deco & DECO_STRIKETHROUGH); + + return glyph; } /** * \brief Get kerning for the pair of glyphs. **/ -FT_Vector ass_font_get_kerning(ass_font_t* font, uint32_t c1, uint32_t c2) +FT_Vector ass_font_get_kerning(ass_font_t *font, uint32_t c1, uint32_t c2) { - FT_Vector v = {0, 0}; - int i; + FT_Vector v = { 0, 0 }; + int i; - for (i = 0; i < font->n_faces; ++i) { - FT_Face face = font->faces[i]; - int i1 = FT_Get_Char_Index(face, c1); - int i2 = FT_Get_Char_Index(face, c2); - if (i1 && i2) { - if (FT_HAS_KERNING(face)) - FT_Get_Kerning(face, i1, i2, FT_KERNING_DEFAULT, &v); - return v; - } - if (i1 || i2) // these glyphs are from different font faces, no kerning information - return v; - } - return v; + for (i = 0; i < font->n_faces; ++i) { + FT_Face face = font->faces[i]; + int i1 = FT_Get_Char_Index(face, c1); + int i2 = FT_Get_Char_Index(face, c2); + if (i1 && i2) { + if (FT_HAS_KERNING(face)) + FT_Get_Kerning(face, i1, i2, FT_KERNING_DEFAULT, &v); + return v; + } + if (i1 || i2) // these glyphs are from different font faces, no kerning information + return v; + } + return v; } /** * \brief Deallocate ass_font_t **/ -void ass_font_free(ass_font_t* font) +void ass_font_free(ass_font_t *font) { - int i; - for (i = 0; i < font->n_faces; ++i) - if (font->faces[i]) FT_Done_Face(font->faces[i]); - if (font->desc.family) free(font->desc.family); - free(font); + int i; + for (i = 0; i < font->n_faces; ++i) + if (font->faces[i]) + FT_Done_Face(font->faces[i]); + if (font->desc.family) + free(font->desc.family); + free(font); } diff --git a/aegisub/libass/ass_font.h b/aegisub/libass/ass_font.h index 520431821..8fe4c1e65 100644 --- a/aegisub/libass/ass_font.h +++ b/aegisub/libass/ass_font.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 * @@ -29,32 +27,40 @@ #include "ass.h" #include "ass_types.h" +#define ASS_FONT_MAX_FACES 10 +#define DECO_UNDERLINE 1 +#define DECO_STRIKETHROUGH 2 + typedef struct ass_font_desc_s { - char* family; - unsigned bold; - unsigned italic; - int treat_family_as_pattern; + char *family; + unsigned bold; + unsigned italic; + int treat_family_as_pattern; } ass_font_desc_t; -#define ASS_FONT_MAX_FACES 10 - typedef struct ass_font_s { - ass_font_desc_t desc; - ass_library_t* library; - FT_Library ftlibrary; - FT_Face faces[ASS_FONT_MAX_FACES]; - int n_faces; - double scale_x, scale_y; // current transform - FT_Vector v; // current shift - double size; + ass_font_desc_t desc; + ass_library_t *library; + FT_Library ftlibrary; + FT_Face faces[ASS_FONT_MAX_FACES]; + int n_faces; + double scale_x, scale_y; // current transform + FT_Vector v; // current shift + double size; } ass_font_t; -ass_font_t* ass_font_new(ass_library_t* library, FT_Library ftlibrary, void* fc_priv, ass_font_desc_t* desc); -void ass_font_set_transform(ass_font_t* font, double scale_x, double scale_y, FT_Vector* v); -void ass_font_set_size(ass_font_t* font, double size); -void ass_font_get_asc_desc(ass_font_t* font, uint32_t ch, int* asc, int* desc); -FT_Glyph ass_font_get_glyph(void* fontconfig_priv, ass_font_t* font, uint32_t ch, ass_hinting_t hinting); -FT_Vector ass_font_get_kerning(ass_font_t* font, uint32_t c1, uint32_t c2); -void ass_font_free(ass_font_t* font); +// FIXME: passing the hashmap via a void pointer is very ugly. +ass_font_t *ass_font_new(void *font_cache, ass_library_t *library, + FT_Library ftlibrary, void *fc_priv, + ass_font_desc_t *desc); +void ass_font_set_transform(ass_font_t *font, double scale_x, + double scale_y, FT_Vector * v); +void ass_font_set_size(ass_font_t *font, double size); +void ass_font_get_asc_desc(ass_font_t *font, uint32_t ch, int *asc, + int *desc); +FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ass_font_t *font, + uint32_t ch, ass_hinting_t hinting, int flags); +FT_Vector ass_font_get_kerning(ass_font_t *font, uint32_t c1, uint32_t c2); +void ass_font_free(ass_font_t *font); -#endif /* LIBASS_FONT_H */ +#endif /* LIBASS_FONT_H */ diff --git a/aegisub/libass/ass_fontconfig.c b/aegisub/libass/ass_fontconfig.c index 854a5ad10..2e6377a99 100644 --- a/aegisub/libass/ass_fontconfig.c +++ b/aegisub/libass/ass_fontconfig.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 * @@ -32,16 +30,11 @@ #include #include FT_FREETYPE_H -#include "mputils.h" +#include "ass_utils.h" #include "ass.h" #include "ass_library.h" #include "ass_fontconfig.h" -#ifdef BUILD_DARWIN -#include "../src/libosxutil/libosxutil.h" -#include -#endif - #ifdef CONFIG_FONTCONFIG #include #include @@ -49,11 +42,11 @@ struct fc_instance_s { #ifdef CONFIG_FONTCONFIG - FcConfig* config; + FcConfig *config; #endif - char* family_default; - char* path_default; - int index_default; + char *family_default; + char *path_default; + int index_default; }; #ifdef CONFIG_FONTCONFIG @@ -77,157 +70,163 @@ struct fc_instance_s { * \param index out: font index inside a file * \param code: the character that should be present in the font, can be 0 * \return font file path -*/ -static char* _select_font(fc_instance_t* priv, const char* family, int treat_family_as_pattern, - unsigned bold, unsigned italic, int* index, uint32_t code) +*/ +static char *_select_font(fc_instance_t *priv, const char *family, + int treat_family_as_pattern, unsigned bold, + unsigned italic, int *index, uint32_t code) { - FcBool rc; - FcResult result; - FcPattern *pat = NULL, *rpat = NULL; - int r_index, r_slant, r_weight; - FcChar8 *r_family, *r_style, *r_file, *r_fullname; - FcBool r_outline, r_embolden; - FcCharSet* r_charset; - FcFontSet* fset = NULL; - int curf; - char* retval = NULL; - int family_cnt; - - *index = 0; + FcBool rc; + FcResult result; + FcPattern *pat = NULL, *rpat = NULL; + int r_index, r_slant, r_weight; + FcChar8 *r_family, *r_style, *r_file, *r_fullname; + FcBool r_outline, r_embolden; + FcCharSet *r_charset; + FcFontSet *fset = NULL; + int curf; + char *retval = NULL; + int family_cnt = 0; - if (treat_family_as_pattern) - pat = FcNameParse((const FcChar8*)family); - else - pat = FcPatternCreate(); + *index = 0; - if (!pat) - goto error; - - if (!treat_family_as_pattern) { - FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)family); + if (treat_family_as_pattern) + pat = FcNameParse((const FcChar8 *) family); + else + pat = FcPatternCreate(); - // In SSA/ASS fonts are sometimes referenced by their "full name", - // which is usually a concatenation of family name and font - // style (ex. Ottawa Bold). Full name is available from - // FontConfig pattern element FC_FULLNAME, but it is never - // used for font matching. - // Therefore, I'm removing words from the end of the name one - // by one, and adding shortened names to the pattern. It seems - // that the first value (full name in this case) has - // precedence in matching. - // An alternative approach could be to reimplement FcFontSort - // using FC_FULLNAME instead of FC_FAMILY. - family_cnt = 1; - { - char* s = strdup(family); - char* p = s + strlen(s); - while (--p > s) - if (*p == ' ' || *p == '-') { - *p = '\0'; - FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)s); - ++ family_cnt; - } - free(s); - } - } - FcPatternAddBool(pat, FC_OUTLINE, FcTrue); - FcPatternAddInteger(pat, FC_SLANT, italic); - FcPatternAddInteger(pat, FC_WEIGHT, bold); + if (!pat) + goto error; - FcDefaultSubstitute(pat); - - rc = FcConfigSubstitute(priv->config, pat, FcMatchPattern); - if (!rc) - goto error; + if (!treat_family_as_pattern) { + FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *) family); - fset = FcFontSort(priv->config, pat, FcTrue, NULL, &result); - if (!fset) - goto error; + // In SSA/ASS fonts are sometimes referenced by their "full name", + // which is usually a concatenation of family name and font + // style (ex. Ottawa Bold). Full name is available from + // FontConfig pattern element FC_FULLNAME, but it is never + // used for font matching. + // Therefore, I'm removing words from the end of the name one + // by one, and adding shortened names to the pattern. It seems + // that the first value (full name in this case) has + // precedence in matching. + // An alternative approach could be to reimplement FcFontSort + // using FC_FULLNAME instead of FC_FAMILY. + family_cnt = 1; + { + char *s = strdup(family); + char *p = s + strlen(s); + while (--p > s) + if (*p == ' ' || *p == '-') { + *p = '\0'; + FcPatternAddString(pat, FC_FAMILY, (const FcChar8 *) s); + ++family_cnt; + } + free(s); + } + } + FcPatternAddBool(pat, FC_OUTLINE, FcTrue); + FcPatternAddInteger(pat, FC_SLANT, italic); + FcPatternAddInteger(pat, FC_WEIGHT, bold); - for (curf = 0; curf < fset->nfont; ++curf) { - FcPattern* curp = fset->fonts[curf]; + FcDefaultSubstitute(pat); - result = FcPatternGetBool(curp, FC_OUTLINE, 0, &r_outline); - if (result != FcResultMatch) - continue; - if (r_outline != FcTrue) - continue; - if (!code) - break; - result = FcPatternGetCharSet(curp, FC_CHARSET, 0, &r_charset); - if (result != FcResultMatch) - continue; - if (FcCharSetHasChar(r_charset, code)) - break; - } + rc = FcConfigSubstitute(priv->config, pat, FcMatchPattern); + if (!rc) + goto error; - if (curf >= fset->nfont) - goto error; + fset = FcFontSort(priv->config, pat, FcTrue, NULL, &result); + if (!fset) + goto error; + + for (curf = 0; curf < fset->nfont; ++curf) { + FcPattern *curp = fset->fonts[curf]; + + result = FcPatternGetBool(curp, FC_OUTLINE, 0, &r_outline); + if (result != FcResultMatch) + continue; + if (r_outline != FcTrue) + continue; + if (!code) + break; + result = FcPatternGetCharSet(curp, FC_CHARSET, 0, &r_charset); + if (result != FcResultMatch) + continue; + if (FcCharSetHasChar(r_charset, code)) + break; + } + + if (curf >= fset->nfont) + goto error; #if (FC_VERSION >= 20297) - if (!treat_family_as_pattern) { - // Remove all extra family names from original pattern. - // After this, FcFontRenderPrepare will select the most relevant family - // name in case there are more than one of them. - for (; family_cnt > 1; --family_cnt) - FcPatternRemove(pat, FC_FAMILY, family_cnt - 1); - } + if (!treat_family_as_pattern) { + // Remove all extra family names from original pattern. + // After this, FcFontRenderPrepare will select the most relevant family + // name in case there are more than one of them. + for (; family_cnt > 1; --family_cnt) + FcPatternRemove(pat, FC_FAMILY, family_cnt - 1); + } #endif - rpat = FcFontRenderPrepare(priv->config, pat, fset->fonts[curf]); - if (!rpat) - goto error; + rpat = FcFontRenderPrepare(priv->config, pat, fset->fonts[curf]); + if (!rpat) + goto error; - result = FcPatternGetInteger(rpat, FC_INDEX, 0, &r_index); - if (result != FcResultMatch) - goto error; - *index = r_index; + result = FcPatternGetInteger(rpat, FC_INDEX, 0, &r_index); + if (result != FcResultMatch) + goto error; + *index = r_index; - result = FcPatternGetString(rpat, FC_FILE, 0, &r_file); - if (result != FcResultMatch) - goto error; - retval = strdup((const char*)r_file); + result = FcPatternGetString(rpat, FC_FILE, 0, &r_file); + if (result != FcResultMatch) + goto error; + retval = strdup((const char *) r_file); - result = FcPatternGetString(rpat, FC_FAMILY, 0, &r_family); - if (result != FcResultMatch) - r_family = NULL; + result = FcPatternGetString(rpat, FC_FAMILY, 0, &r_family); + if (result != FcResultMatch) + r_family = NULL; - result = FcPatternGetString(rpat, FC_FULLNAME, 0, &r_fullname); - if (result != FcResultMatch) - r_fullname = NULL; + result = FcPatternGetString(rpat, FC_FULLNAME, 0, &r_fullname); + if (result != FcResultMatch) + r_fullname = NULL; - if (!treat_family_as_pattern && - !(r_family && strcasecmp((const char*)r_family, family) == 0) && - !(r_fullname && strcasecmp((const char*)r_fullname, family) == 0)) - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne, - (const char*)(r_fullname ? r_fullname : r_family), family); + if (!treat_family_as_pattern && + !(r_family && strcasecmp((const char *) r_family, family) == 0) && + !(r_fullname && strcasecmp((const char *) r_fullname, family) == 0)) + ass_msg(MSGL_WARN, + MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne, + (const char *) (r_fullname ? r_fullname : r_family), family); - result = FcPatternGetString(rpat, FC_STYLE, 0, &r_style); - if (result != FcResultMatch) - r_style = NULL; + result = FcPatternGetString(rpat, FC_STYLE, 0, &r_style); + if (result != FcResultMatch) + r_style = NULL; - result = FcPatternGetInteger(rpat, FC_SLANT, 0, &r_slant); - if (result != FcResultMatch) - r_slant = 0; + result = FcPatternGetInteger(rpat, FC_SLANT, 0, &r_slant); + if (result != FcResultMatch) + r_slant = 0; - result = FcPatternGetInteger(rpat, FC_WEIGHT, 0, &r_weight); - if (result != FcResultMatch) - r_weight = 0; + result = FcPatternGetInteger(rpat, FC_WEIGHT, 0, &r_weight); + if (result != FcResultMatch) + r_weight = 0; - result = FcPatternGetBool(rpat, FC_EMBOLDEN, 0, &r_embolden); - if (result != FcResultMatch) - r_embolden = 0; + result = FcPatternGetBool(rpat, FC_EMBOLDEN, 0, &r_embolden); + if (result != FcResultMatch) + r_embolden = 0; - mp_msg(MSGT_ASS, MSGL_V, "[ass] Font info: family '%s', style '%s', fullname '%s'," - " slant %d, weight %d%s\n", - (const char*)r_family, (const char*)r_style, (const char*)r_fullname, - r_slant, r_weight, r_embolden ? ", embolden" : ""); + ass_msg(MSGL_V, + "[ass] Font info: family '%s', style '%s', fullname '%s'," + " slant %d, weight %d%s\n", (const char *) r_family, + (const char *) r_style, (const char *) r_fullname, r_slant, + r_weight, r_embolden ? ", embolden" : ""); - error: - if (pat) FcPatternDestroy(pat); - if (rpat) FcPatternDestroy(rpat); - if (fset) FcFontSetDestroy(fset); - return retval; + error: + if (pat) + FcPatternDestroy(pat); + if (rpat) + FcPatternDestroy(rpat); + if (fset) + FcFontSetDestroy(fset); + return retval; } /** @@ -240,76 +239,79 @@ static char* _select_font(fc_instance_t* priv, const char* family, int treat_fam * \param index out: font index inside a file * \param code: the character that should be present in the font, can be 0 * \return font file path -*/ -char* fontconfig_select(fc_instance_t* priv, const char* family, int treat_family_as_pattern, - unsigned bold, unsigned italic, int* index, uint32_t code) +*/ +char *fontconfig_select(fc_instance_t *priv, const char *family, + int treat_family_as_pattern, unsigned bold, + unsigned italic, int *index, uint32_t code) { - char* res = 0; - if (!priv->config) { - *index = priv->index_default; - return priv->path_default; - } - if (family && *family) - res = _select_font(priv, family, treat_family_as_pattern, bold, italic, index, code); - if (!res && priv->family_default) { - res = _select_font(priv, priv->family_default, 0, bold, italic, index, code); - if (res) - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingDefaultFontFamily, - family, bold, italic, res, *index); - } - if (!res && priv->path_default) { - res = priv->path_default; - *index = priv->index_default; - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingDefaultFont, - family, bold, italic, res, *index); - } - if (!res) { - res = _select_font(priv, "Arial", 0, bold, italic, index, code); - if (res) - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingArialFontFamily, - family, bold, italic, res, *index); - } - if (res) - mp_msg(MSGT_ASS, MSGL_V, "fontconfig_select: (%s, %d, %d) -> %s, %d\n", - family, bold, italic, res, *index); - return res; + char *res = 0; + if (!priv->config) { + *index = priv->index_default; + return priv->path_default; + } + if (family && *family) + res = + _select_font(priv, family, treat_family_as_pattern, bold, + italic, index, code); + if (!res && priv->family_default) { + res = + _select_font(priv, priv->family_default, 0, bold, italic, index, + code); + if (res) + ass_msg(MSGL_WARN, MSGTR_LIBASS_UsingDefaultFontFamily, + family, bold, italic, res, *index); + } + if (!res && priv->path_default) { + res = priv->path_default; + *index = priv->index_default; + ass_msg(MSGL_WARN, MSGTR_LIBASS_UsingDefaultFont, + family, bold, italic, res, *index); + } + if (!res) { + res = _select_font(priv, "Arial", 0, bold, italic, index, code); + if (res) + ass_msg(MSGL_WARN, MSGTR_LIBASS_UsingArialFontFamily, + family, bold, italic, res, *index); + } + if (res) + ass_msg(MSGL_V, + "fontconfig_select: (%s, %d, %d) -> %s, %d\n", family, bold, + italic, res, *index); + return res; } #if (FC_VERSION < 20402) -static char* validate_fname(char* name) +static char *validate_fname(char *name) { - char* fname; - char* p; - char* q; - unsigned code; - int sz = strlen(name); + char *fname; + char *p; + char *q; + unsigned code; + int sz = strlen(name); - q = fname = malloc(sz + 1); - p = name; - while (*p) { - code = utf8_get_char(&p); - if (code == 0) - break; - if ( (code > 0x7F) || - (code == '\\') || - (code == '/') || - (code == ':') || - (code == '*') || - (code == '?') || - (code == '<') || - (code == '>') || - (code == '|') || - (code == 0)) - { - *q++ = '_'; - } else { - *q++ = code; - } - if (p - name > sz) - break; - } - *q = 0; - return fname; + q = fname = malloc(sz + 1); + p = name; + while (*p) { + code = ass_utf8_get_char(&p); + if (code == 0) + break; + if ((code > 0x7F) || + (code == '\\') || + (code == '/') || + (code == ':') || + (code == '*') || + (code == '?') || + (code == '<') || + (code == '>') || (code == '|') || (code == 0)) { + *q++ = '_'; + } else { + *q++ = code; + } + if (p - name > sz) + break; + } + *q = 0; + return fname; } #endif @@ -321,87 +323,97 @@ static char* validate_fname(char* name) * \param idx index of the processed font in library->fontdata * With FontConfig >= 2.4.2, builds a font pattern in memory via FT_New_Memory_Face/FcFreeTypeQueryFace. * With older FontConfig versions, save the font to ~/.mplayer/fonts. -*/ -static void process_fontdata(fc_instance_t* priv, ass_library_t* library, FT_Library ftlibrary, int idx) +*/ +static void process_fontdata(fc_instance_t *priv, ass_library_t *library, + FT_Library ftlibrary, int idx) { - int rc; - const char* name = library->fontdata[idx].name; - const char* data = library->fontdata[idx].data; - int data_size = library->fontdata[idx].size; + int rc; + const char *name = library->fontdata[idx].name; + const char *data = library->fontdata[idx].data; + int data_size = library->fontdata[idx].size; #if (FC_VERSION < 20402) - struct stat st; - char* fname; - const char* fonts_dir = library->fonts_dir; - char buf[1000]; - FILE* fp = NULL; + struct stat st; + char *fname; + const char *fonts_dir = library->fonts_dir; + char buf[1000]; + FILE *fp = NULL; - if (!fonts_dir) - return; - rc = stat(fonts_dir, &st); - if (rc) { - int res; + if (!fonts_dir) + return; + rc = stat(fonts_dir, &st); + if (rc) { + int res; #ifndef __MINGW32__ - res = mkdir(fonts_dir, 0700); + res = mkdir(fonts_dir, 0700); #else - res = mkdir(fonts_dir); + res = mkdir(fonts_dir); #endif - if (res) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FailedToCreateDirectory, fonts_dir); - } - } else if (!S_ISDIR(st.st_mode)) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NotADirectory, fonts_dir); - } - - fname = validate_fname((char*)name); + if (res) { + ass_msg(MSGL_WARN, + MSGTR_LIBASS_FailedToCreateDirectory, fonts_dir); + } + } else if (!S_ISDIR(st.st_mode)) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_NotADirectory, fonts_dir); + } - snprintf(buf, 1000, "%s/%s", fonts_dir, fname); - free(fname); + fname = validate_fname((char *) name); - fp = fopen(buf, "wb"); - if (!fp) return; + snprintf(buf, 1000, "%s/%s", fonts_dir, fname); + free(fname); - fwrite(data, data_size, 1, fp); - fclose(fp); + fp = fopen(buf, "wb"); + if (!fp) + return; -#else // (FC_VERSION >= 20402) - FT_Face face; - FcPattern* pattern; - FcFontSet* fset; - FcBool res; - int face_index, num_faces = 1; + fwrite(data, data_size, 1, fp); + fclose(fp); - for (face_index = 0; face_index < num_faces; ++face_index) { - rc = FT_New_Memory_Face(ftlibrary, (unsigned char*)data, data_size, face_index, &face); - if (rc) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_ErrorOpeningMemoryFont, name); - return; - } - num_faces = face->num_faces; +#else // (FC_VERSION >= 20402) + FT_Face face; + FcPattern *pattern; + FcFontSet *fset; + FcBool res; + int face_index, num_faces = 1; - pattern = FcFreeTypeQueryFace(face, (unsigned char*)name, 0, FcConfigGetBlanks(priv->config)); - if (!pattern) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcFreeTypeQueryFace"); - FT_Done_Face(face); - return; - } + for (face_index = 0; face_index < num_faces; ++face_index) { + rc = FT_New_Memory_Face(ftlibrary, (unsigned char *) data, + data_size, face_index, &face); + if (rc) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_ErrorOpeningMemoryFont, + name); + return; + } + num_faces = face->num_faces; - fset = FcConfigGetFonts(priv->config, FcSetSystem); // somehow it failes when asked for FcSetApplication - if (!fset) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcConfigGetFonts"); - FT_Done_Face(face); - return; - } + pattern = + FcFreeTypeQueryFace(face, (unsigned char *) name, 0, + FcConfigGetBlanks(priv->config)); + if (!pattern) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, + "FcFreeTypeQueryFace"); + FT_Done_Face(face); + return; + } - res = FcFontSetAdd(fset, pattern); - if (!res) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, "FcFontSetAdd"); - FT_Done_Face(face); - return; - } + fset = FcConfigGetFonts(priv->config, FcSetSystem); // somehow it failes when asked for FcSetApplication + if (!fset) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, + "FcConfigGetFonts"); + FT_Done_Face(face); + return; + } - FT_Done_Face(face); - } + res = FcFontSetAdd(fset, pattern); + if (!res) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_FunctionCallFailed, + "FcFontSetAdd"); + FT_Done_Face(face); + return; + } + + FT_Done_Face(face); + } #endif } @@ -412,128 +424,131 @@ static void process_fontdata(fc_instance_t* priv, ass_library_t* library, FT_Lib * \param family default font family * \param path default font path * \return pointer to fontconfig private data -*/ -fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, const char* family, const char* path, int fc) +*/ +fc_instance_t *fontconfig_init(ass_library_t *library, + FT_Library ftlibrary, const char *family, + const char *path, int fc, const char *config) { - int rc; - fc_instance_t* priv = calloc(1, sizeof(fc_instance_t)); - const char* dir = library->fonts_dir; -#ifdef BUILD_DARWIN - char config_path[MAXPATHLEN]; - char *config_dir; -#endif - int i; + int rc; + fc_instance_t *priv = calloc(1, sizeof(fc_instance_t)); + const char *dir = library->fonts_dir; + int i; - if (!fc) { - mp_msg(MSGT_ASS, MSGL_WARN, - MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed); - goto exit; - } + if (!fc) { + ass_msg(MSGL_WARN, + MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed); + goto exit; + } -#ifdef BUILD_DARWIN - config_dir = OSX_GetBundleResourcesDirectory(); - snprintf(config_path, MAXPATHLEN, "%s/etc/fonts/fonts.conf", config_dir); - free(config_dir); - priv->config = FcConfigCreate(); - rc = FcConfigParseAndLoad(priv->config, config_path, FcTrue); - FcConfigBuildFonts(priv->config); - FcConfigSetCurrent(priv->config); + if (config) { + priv->config = FcConfigCreate(); + rc = FcConfigParseAndLoad(priv->config, (unsigned char *)config, + FcTrue); + FcConfigBuildFonts(priv->config); + FcConfigSetCurrent(priv->config); + } else { + rc = FcInit(); + assert(rc); + priv->config = FcConfigGetCurrent(); + } - if (!rc) { -#else - rc = FcInit(); - assert(rc); + if (!rc || !priv->config) { + ass_msg(MSGL_FATAL, + MSGTR_LIBASS_FcInitLoadConfigAndFontsFailed); + goto exit; + } - priv->config = FcConfigGetCurrent(); - if (!priv->config) { -#endif - mp_msg(MSGT_ASS, MSGL_FATAL, MSGTR_LIBASS_FcInitLoadConfigAndFontsFailed); - goto exit; - } + for (i = 0; i < library->num_fontdata; ++i) + process_fontdata(priv, library, ftlibrary, i); - for (i = 0; i < library->num_fontdata; ++i) - process_fontdata(priv, library, ftlibrary, i); + if (dir) { + if (FcDirCacheValid((const FcChar8 *) dir) == FcFalse) { + ass_msg(MSGL_INFO, MSGTR_LIBASS_UpdatingFontCache); + if (FcGetVersion() >= 20390 && FcGetVersion() < 20400) + ass_msg(MSGL_WARN, + MSGTR_LIBASS_BetaVersionsOfFontconfigAreNotSupported); + // FontConfig >= 2.4.0 updates cache automatically in FcConfigAppFontAddDir() + if (FcGetVersion() < 20390) { + FcFontSet *fcs; + FcStrSet *fss; + fcs = FcFontSetCreate(); + fss = FcStrSetCreate(); + rc = FcStrSetAdd(fss, (const FcChar8 *) dir); + if (!rc) { + ass_msg(MSGL_WARN, + MSGTR_LIBASS_FcStrSetAddFailed); + goto ErrorFontCache; + } - if (dir) { - if (FcDirCacheValid((const FcChar8 *)dir) == FcFalse) - { - mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_UpdatingFontCache); - if (FcGetVersion() >= 20390 && FcGetVersion() < 20400) - mp_msg(MSGT_ASS, MSGL_WARN, - MSGTR_LIBASS_BetaVersionsOfFontconfigAreNotSupported); - // FontConfig >= 2.4.0 updates cache automatically in FcConfigAppFontAddDir() - if (FcGetVersion() < 20390) { - FcFontSet* fcs; - FcStrSet* fss; - fcs = FcFontSetCreate(); - fss = FcStrSetCreate(); - rc = FcStrSetAdd(fss, (const FcChar8*)dir); - if (!rc) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcStrSetAddFailed); - goto ErrorFontCache; - } + rc = FcDirScan(fcs, fss, NULL, + FcConfigGetBlanks(priv->config), + (const FcChar8 *) dir, FcFalse); + if (!rc) { + ass_msg(MSGL_WARN, + MSGTR_LIBASS_FcDirScanFailed); + goto ErrorFontCache; + } - rc = FcDirScan(fcs, fss, NULL, FcConfigGetBlanks(priv->config), - (const FcChar8 *)dir, FcFalse); - if (!rc) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcDirScanFailed); - goto ErrorFontCache; - } + rc = FcDirSave(fcs, fss, (const FcChar8 *) dir); + if (!rc) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_FcDirSave); + goto ErrorFontCache; + } + ErrorFontCache: + ; + } + } - rc = FcDirSave(fcs, fss, (const FcChar8 *)dir); - if (!rc) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcDirSave); - goto ErrorFontCache; - } - ErrorFontCache: - ; - } - } + rc = FcConfigAppFontAddDir(priv->config, (const FcChar8 *) dir); + if (!rc) { + ass_msg(MSGL_WARN, + MSGTR_LIBASS_FcConfigAppFontAddDirFailed); + } + } - rc = FcConfigAppFontAddDir(priv->config, (const FcChar8*)dir); - if (!rc) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FcConfigAppFontAddDirFailed); - } - } + priv->family_default = family ? strdup(family) : NULL; + exit: + priv->path_default = path ? strdup(path) : NULL; + priv->index_default = 0; - priv->family_default = family ? strdup(family) : NULL; -exit: - priv->path_default = path ? strdup(path) : NULL; - priv->index_default = 0; - - return priv; + return priv; } -#else /* CONFIG_FONTCONFIG */ +#else /* CONFIG_FONTCONFIG */ -char* fontconfig_select(fc_instance_t* priv, const char* family, int treat_family_as_pattern, - unsigned bold, unsigned italic, int* index, uint32_t code) +char *fontconfig_select(fc_instance_t *priv, const char *family, + int treat_family_as_pattern, unsigned bold, + unsigned italic, int *index, uint32_t code) { - *index = priv->index_default; - return priv->path_default; + *index = priv->index_default; + return priv->path_default; } -fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, const char* family, const char* path, int fc) +fc_instance_t *fontconfig_init(ass_library_t *library, + FT_Library ftlibrary, const char *family, + const char *path, int fc) { - fc_instance_t* priv; + fc_instance_t *priv; - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed); - - priv = calloc(1, sizeof(fc_instance_t)); - - priv->path_default = strdup(path); - priv->index_default = 0; - return priv; + ass_msg(MSGL_WARN, + MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed); + + priv = calloc(1, sizeof(fc_instance_t)); + + priv->path_default = strdup(path); + priv->index_default = 0; + return priv; } #endif -void fontconfig_done(fc_instance_t* priv) +void fontconfig_done(fc_instance_t *priv) { - // don't call FcFini() here, library can still be used by some code - if (priv && priv->path_default) free(priv->path_default); - if (priv && priv->family_default) free(priv->family_default); - if (priv) free(priv); + // don't call FcFini() here, library can still be used by some code + if (priv && priv->path_default) + free(priv->path_default); + if (priv && priv->family_default) + free(priv->family_default); + if (priv) + free(priv); } - - diff --git a/aegisub/libass/ass_fontconfig.h b/aegisub/libass/ass_fontconfig.h index 77806909c..71b2006b0 100644 --- a/aegisub/libass/ass_fontconfig.h +++ b/aegisub/libass/ass_fontconfig.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 * @@ -34,8 +32,12 @@ typedef struct fc_instance_s fc_instance_t; -fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, const char* family, const char* path, int fc); -char* fontconfig_select(fc_instance_t* priv, const char* family, int treat_family_as_pattern, unsigned bold, unsigned italic, int* index, uint32_t code); -void fontconfig_done(fc_instance_t* priv); +fc_instance_t *fontconfig_init(ass_library_t *library, + FT_Library ftlibrary, const char *family, + const char *path, int fc, const char *config); +char *fontconfig_select(fc_instance_t *priv, const char *family, + int treat_family_as_pattern, unsigned bold, + unsigned italic, int *index, uint32_t code); +void fontconfig_done(fc_instance_t *priv); -#endif /* LIBASS_FONTCONFIG_H */ +#endif /* LIBASS_FONTCONFIG_H */ diff --git a/aegisub/libass/ass_library.c b/aegisub/libass/ass_library.c index 8c6af464a..ce877da7e 100644 --- a/aegisub/libass/ass_library.c +++ b/aegisub/libass/ass_library.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 * @@ -29,87 +27,90 @@ #include "ass_library.h" -ass_library_t* ass_library_init(void) +ass_library_t *ass_library_init(void) { - return calloc(1, sizeof(ass_library_t)); + return calloc(1, sizeof(ass_library_t)); } -void ass_library_done(ass_library_t* priv) +void ass_library_done(ass_library_t *priv) { - if (priv) { - ass_set_fonts_dir(priv, NULL); - ass_set_style_overrides(priv, NULL); - ass_clear_fonts(priv); - free(priv); - } + if (priv) { + ass_set_fonts_dir(priv, NULL); + ass_set_style_overrides(priv, NULL); + ass_clear_fonts(priv); + free(priv); + } } -void ass_set_fonts_dir(ass_library_t* priv, const char* fonts_dir) +void ass_set_fonts_dir(ass_library_t *priv, const char *fonts_dir) { - if (priv->fonts_dir) - free(priv->fonts_dir); + if (priv->fonts_dir) + free(priv->fonts_dir); - priv->fonts_dir = fonts_dir ? strdup(fonts_dir) : 0; + priv->fonts_dir = fonts_dir ? strdup(fonts_dir) : 0; } -void ass_set_extract_fonts(ass_library_t* priv, int extract) +void ass_set_extract_fonts(ass_library_t *priv, int extract) { - priv->extract_fonts = !!extract; + priv->extract_fonts = !!extract; } -void ass_set_style_overrides(ass_library_t* priv, char** list) +void ass_set_style_overrides(ass_library_t *priv, char **list) { - char** p; - char** q; - int cnt; - - if (priv->style_overrides) { - for (p = priv->style_overrides; *p; ++p) - free(*p); - free(priv->style_overrides); - } - - if (!list) return; + char **p; + char **q; + int cnt; - for (p = list, cnt = 0; *p; ++p, ++cnt) {} + if (priv->style_overrides) { + for (p = priv->style_overrides; *p; ++p) + free(*p); + free(priv->style_overrides); + } - priv->style_overrides = malloc((cnt + 1) * sizeof(char*)); - for (p = list, q = priv->style_overrides; *p; ++p, ++q) - *q = strdup(*p); - priv->style_overrides[cnt] = NULL; + if (!list) + return; + + for (p = list, cnt = 0; *p; ++p, ++cnt) { + } + + priv->style_overrides = malloc((cnt + 1) * sizeof(char *)); + for (p = list, q = priv->style_overrides; *p; ++p, ++q) + *q = strdup(*p); + priv->style_overrides[cnt] = NULL; } static void grow_array(void **array, int nelem, size_t elsize) { - if (!(nelem & 31)) - *array = realloc(*array, (nelem + 32) * elsize); + if (!(nelem & 31)) + *array = realloc(*array, (nelem + 32) * elsize); } -void ass_add_font(ass_library_t* priv, char* name, char* data, int size) +void ass_add_font(ass_library_t *priv, char *name, char *data, int size) { - int idx = priv->num_fontdata; - if (!name || !data || !size) - return; - grow_array((void**)&priv->fontdata, priv->num_fontdata, sizeof(*priv->fontdata)); - - priv->fontdata[idx].name = strdup(name); - - priv->fontdata[idx].data = malloc(size); - memcpy(priv->fontdata[idx].data, data, size); - - priv->fontdata[idx].size = size; - - priv->num_fontdata ++; + int idx = priv->num_fontdata; + if (!name || !data || !size) + return; + grow_array((void **) &priv->fontdata, priv->num_fontdata, + sizeof(*priv->fontdata)); + + priv->fontdata[idx].name = strdup(name); + + priv->fontdata[idx].data = malloc(size); + memcpy(priv->fontdata[idx].data, data, size); + + priv->fontdata[idx].size = size; + + priv->num_fontdata++; } -void ass_clear_fonts(ass_library_t* priv) +void ass_clear_fonts(ass_library_t *priv) { - int i; - for (i = 0; i < priv->num_fontdata; ++i) { - free(priv->fontdata[i].name); - free(priv->fontdata[i].data); - } - free(priv->fontdata); - priv->fontdata = NULL; - priv->num_fontdata = 0; + int i; + for (i = 0; i < priv->num_fontdata; ++i) { + free(priv->fontdata[i].name); + free(priv->fontdata[i].data); + } + free(priv->fontdata); + priv->fontdata = NULL; + priv->num_fontdata = 0; } diff --git a/aegisub/libass/ass_library.h b/aegisub/libass/ass_library.h index ecf46f342..48ffefe8b 100644 --- a/aegisub/libass/ass_library.h +++ b/aegisub/libass/ass_library.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,18 +22,18 @@ #define LIBASS_LIBRARY_H typedef struct ass_fontdata_s { - char* name; - char* data; - int size; + char *name; + char *data; + int size; } ass_fontdata_t; struct ass_library_s { - char* fonts_dir; - int extract_fonts; - char** style_overrides; + char *fonts_dir; + int extract_fonts; + char **style_overrides; - ass_fontdata_t* fontdata; - int num_fontdata; + ass_fontdata_t *fontdata; + int num_fontdata; }; -#endif /* LIBASS_LIBRARY_H */ +#endif /* LIBASS_LIBRARY_H */ diff --git a/aegisub/libass/ass_render.c b/aegisub/libass/ass_render.c index fd3a122e9..e2eac7b4a 100644 --- a/aegisub/libass/ass_render.c +++ b/aegisub/libass/ass_render.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 * @@ -31,8 +29,6 @@ #include FT_GLYPH_H #include FT_SYNTHESIS_H -#include "mputils.h" - #include "ass.h" #include "ass_font.h" #include "ass_bitmap.h" @@ -40,292 +36,449 @@ #include "ass_utils.h" #include "ass_fontconfig.h" #include "ass_library.h" +#include "ass_drawing.h" -#define MAX_GLYPHS 3000 -#define MAX_LINES 300 -#define BLUR_MAX_RADIUS 50.0 +#define MAX_GLYPHS_INITIAL 1024 +#define MAX_LINES_INITIAL 64 +#define BLUR_MAX_RADIUS 100.0 #define MAX_BE 100 -#define ROUND(x) ((int) ((x) + .5)) -#define SUBPIXEL_MASK 56 // d6 bitmask for subpixel accuracy adjustment +#define SUBPIXEL_MASK 63 +#define SUBPIXEL_ACCURACY 7 // d6 mask for subpixel accuracy adjustment static int last_render_id = 0; -typedef struct ass_settings_s { - int frame_width; - int frame_height; - double font_size_coeff; // font size multiplier - double line_spacing; // additional line spacing (in frame pixels) - int top_margin; // height of top margin. Everything except toptitles is shifted down by top_margin. - int bottom_margin; // height of bottom margin. (frame_height - top_margin - bottom_margin) is original video height. - int left_margin; - int right_margin; - int use_margins; // 0 - place all subtitles inside original frame - // 1 - use margins for placing toptitles and subtitles - double aspect; // frame aspect ratio, d_width / d_height. - ass_hinting_t hinting; +typedef struct double_bbox_s { + double xMin; + double xMax; + double yMin; + double yMax; +} double_bbox_t; - char* default_font; - char* default_family; +typedef struct double_vector_s { + double x; + double y; +} double_vector_t; + +typedef struct ass_settings_s { + int frame_width; + int frame_height; + double font_size_coeff; // font size multiplier + double line_spacing; // additional line spacing (in frame pixels) + int top_margin; // height of top margin. Everything except toptitles is shifted down by top_margin. + int bottom_margin; // height of bottom margin. (frame_height - top_margin - bottom_margin) is original video height. + int left_margin; + int right_margin; + int use_margins; // 0 - place all subtitles inside original frame + // 1 - use margins for placing toptitles and subtitles + double aspect; // frame aspect ratio, d_width / d_height. + double pixel_ratio; // pixel ratio of the source image + ass_hinting_t hinting; + + char *default_font; + char *default_family; } ass_settings_t; // a rendered event typedef struct event_images_s { - ass_image_t* imgs; - int top, height; - int detect_collisions; - int shift_direction; - ass_event_t* event; + ass_image_t *imgs; + int top, height; + int detect_collisions; + int shift_direction; + ass_event_t *event; } event_images_t; -struct ass_renderer_s { - ass_library_t* library; - FT_Library ftlibrary; - fc_instance_t* fontconfig_priv; - ass_settings_t settings; - int render_id; - ass_synth_priv_t* synth_priv; - - ass_image_t* images_root; // rendering result is stored here - ass_image_t* prev_images_root; - - event_images_t* eimg; // temporary buffer for sorting rendered events - int eimg_size; // allocated buffer size -}; - -typedef enum {EF_NONE = 0, EF_KARAOKE, EF_KARAOKE_KF, EF_KARAOKE_KO} effect_t; +typedef enum { EF_NONE = 0, EF_KARAOKE, EF_KARAOKE_KF, EF_KARAOKE_KO +} effect_t; // describes a glyph // glyph_info_t and text_info_t are used for text centering and word-wrapping operations typedef struct glyph_info_s { - unsigned symbol; - FT_Glyph glyph; - FT_Glyph outline_glyph; - bitmap_t* bm; // glyph bitmap - bitmap_t* bm_o; // outline bitmap - bitmap_t* bm_s; // shadow bitmap - FT_BBox bbox; - FT_Vector pos; - char linebreak; // the first (leading) glyph of some line ? - uint32_t c[4]; // colors - FT_Vector advance; // 26.6 - effect_t effect_type; - int effect_timing; // time duration of current karaoke word - // after process_karaoke_effects: distance in pixels from the glyph origin. - // part of the glyph to the left of it is displayed in a different color. - int effect_skip_timing; // delay after the end of last karaoke word - int asc, desc; // font max ascender and descender -// int height; - int be; // blur edges - double blur; // gaussian blur - double shadow; - double frx, fry, frz; // rotation - - bitmap_hash_key_t hash_key; + unsigned symbol; + FT_Glyph glyph; + FT_Glyph outline_glyph; + bitmap_t *bm; // glyph bitmap + bitmap_t *bm_o; // outline bitmap + bitmap_t *bm_s; // shadow bitmap + FT_BBox bbox; + FT_Vector pos; + char linebreak; // the first (leading) glyph of some line ? + uint32_t c[4]; // colors + FT_Vector advance; // 26.6 + effect_t effect_type; + int effect_timing; // time duration of current karaoke word + // after process_karaoke_effects: distance in pixels from the glyph origin. + // part of the glyph to the left of it is displayed in a different color. + int effect_skip_timing; // delay after the end of last karaoke word + int asc, desc; // font max ascender and descender +// int height; + int be; // blur edges + double blur; // gaussian blur + double shadow_x; + double shadow_y; + double frx, fry, frz; // rotation + double fax, fay; // text shearing + + bitmap_hash_key_t hash_key; } glyph_info_t; typedef struct line_info_s { - int asc, desc; + double asc, desc; } line_info_t; typedef struct text_info_s { - glyph_info_t* glyphs; - int length; - line_info_t lines[MAX_LINES]; - int n_lines; - int height; + glyph_info_t *glyphs; + int length; + line_info_t *lines; + int n_lines; + double height; + int max_glyphs; + int max_lines; } text_info_t; // Renderer state. // Values like current font face, color, screen position, clipping and so on are stored here. typedef struct render_context_s { - ass_event_t* event; - ass_style_t* style; - - ass_font_t* font; - char* font_path; - double font_size; - - FT_Stroker stroker; - int alignment; // alignment overrides go here; if zero, style value will be used - double frx, fry, frz; - enum { EVENT_NORMAL, // "normal" top-, sub- or mid- title - EVENT_POSITIONED, // happens after pos(,), margins are ignored - EVENT_HSCROLL, // "Banner" transition effect, text_width is unlimited - EVENT_VSCROLL // "Scroll up", "Scroll down" transition effects - } evt_type; - double pos_x, pos_y; // position - double org_x, org_y; // origin - char have_origin; // origin is explicitly defined; if 0, get_base_point() is used - double scale_x, scale_y; - double hspacing; // distance between letters, in pixels - double border; // outline width - uint32_t c[4]; // colors(Primary, Secondary, so on) in RGBA - int clip_x0, clip_y0, clip_x1, clip_y1; - char detect_collisions; - uint32_t fade; // alpha from \fad - char be; // blur edges - double blur; // gaussian blur - double shadow; - int drawing_mode; // not implemented; when != 0 text is discarded, except for style override tags + ass_event_t *event; + ass_style_t *style; - effect_t effect_type; - int effect_timing; - int effect_skip_timing; + ass_font_t *font; + char *font_path; + double font_size; + int flags; // decoration flags (underline/strike-through) - enum { SCROLL_LR, // left-to-right - SCROLL_RL, - SCROLL_TB, // top-to-bottom - SCROLL_BT - } scroll_direction; // for EVENT_HSCROLL, EVENT_VSCROLL - int scroll_shift; + FT_Stroker stroker; + int alignment; // alignment overrides go here; if zero, style value will be used + double frx, fry, frz; + double fax, fay; // text shearing + enum { EVENT_NORMAL, // "normal" top-, sub- or mid- title + EVENT_POSITIONED, // happens after pos(,), margins are ignored + EVENT_HSCROLL, // "Banner" transition effect, text_width is unlimited + EVENT_VSCROLL // "Scroll up", "Scroll down" transition effects + } evt_type; + double pos_x, pos_y; // position + double org_x, org_y; // origin + char have_origin; // origin is explicitly defined; if 0, get_base_point() is used + double scale_x, scale_y; + double hspacing; // distance between letters, in pixels + double border_x; // outline width + double border_y; + uint32_t c[4]; // colors(Primary, Secondary, so on) in RGBA + int clip_x0, clip_y0, clip_x1, clip_y1; + char clip_mode; // 1 = iclip + char detect_collisions; + uint32_t fade; // alpha from \fad + char be; // blur edges + double blur; // gaussian blur + double shadow_x; + double shadow_y; + int drawing_mode; // not implemented; when != 0 text is discarded, except for style override tags + ass_drawing_t *drawing; // current drawing + + effect_t effect_type; + int effect_timing; + int effect_skip_timing; + + enum { SCROLL_LR, // left-to-right + SCROLL_RL, + SCROLL_TB, // top-to-bottom + SCROLL_BT + } scroll_direction; // for EVENT_HSCROLL, EVENT_VSCROLL + int scroll_shift; + + // face properties + char *family; + unsigned bold; + unsigned italic; + int treat_family_as_pattern; - // face properties - char* family; - unsigned bold; - unsigned italic; - int treat_family_as_pattern; - } render_context_t; -// frame-global data -typedef struct frame_context_s { - ass_renderer_t* ass_priv; - int width, height; // screen dimensions - int orig_height; // frame height ( = screen height - margins ) - int orig_width; // frame width ( = screen width - margins ) - int orig_height_nocrop; // frame height ( = screen height - margins + cropheight) - int orig_width_nocrop; // frame width ( = screen width - margins + cropwidth) - ass_track_t* track; - long long time; // frame's timestamp, ms - double font_scale; - double font_scale_x; // x scale applied to all glyphs to preserve text aspect ratio - double border_scale; -} frame_context_t; +typedef struct cache_store_s { + hashmap_t *font_cache; + hashmap_t *glyph_cache; + hashmap_t *bitmap_cache; + hashmap_t *composite_cache; +} cache_store_t; -static ass_renderer_t* ass_renderer; -static ass_settings_t* global_settings; -static text_info_t text_info; -static render_context_t render_context; -static frame_context_t frame_context; +struct ass_renderer_s { + ass_library_t *library; + FT_Library ftlibrary; + fc_instance_t *fontconfig_priv; + ass_settings_t settings; + int render_id; + ass_synth_priv_t *synth_priv; -struct render_priv_s { - int top, height; - int render_id; + ass_image_t *images_root; // rendering result is stored here + ass_image_t *prev_images_root; + + event_images_t *eimg; // temporary buffer for sorting rendered events + int eimg_size; // allocated buffer size + + // frame-global data + int width, height; // screen dimensions + int orig_height; // frame height ( = screen height - margins ) + int orig_width; // frame width ( = screen width - margins ) + int orig_height_nocrop; // frame height ( = screen height - margins + cropheight) + int orig_width_nocrop; // frame width ( = screen width - margins + cropwidth) + ass_track_t *track; + long long time; // frame's timestamp, ms + double font_scale; + double font_scale_x; // x scale applied to all glyphs to preserve text aspect ratio + double border_scale; + + render_context_t state; + text_info_t text_info; + cache_store_t cache; }; -static void ass_lazy_track_init(void) +struct render_priv_s { + int top, height; + int render_id; +}; + +static void ass_lazy_track_init(ass_renderer_t *render_priv) { - ass_track_t* track = frame_context.track; - if (track->PlayResX && track->PlayResY) - return; - if (!track->PlayResX && !track->PlayResY) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NeitherPlayResXNorPlayResYDefined); - track->PlayResX = 384; - track->PlayResY = 288; - } else { - double orig_aspect = (global_settings->aspect * frame_context.height * frame_context.orig_width) / - frame_context.orig_height / frame_context.width; - if (!track->PlayResY && track->PlayResX == 1280) { - track->PlayResY = 1024; - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_PlayResYUndefinedSettingY, track->PlayResY); - } else if (!track->PlayResY) { - track->PlayResY = track->PlayResX / orig_aspect + .5; - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_PlayResYUndefinedSettingY, track->PlayResY); - } else if (!track->PlayResX && track->PlayResY == 1024) { - track->PlayResX = 1280; - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_PlayResXUndefinedSettingX, track->PlayResX); - } else if (!track->PlayResX) { - track->PlayResX = track->PlayResY * orig_aspect + .5; - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_PlayResXUndefinedSettingX, track->PlayResX); - } - } + ass_track_t *track = render_priv->track; + ass_settings_t *settings_priv = &render_priv->settings; + + if (track->PlayResX && track->PlayResY) + return; + if (!track->PlayResX && !track->PlayResY) { + ass_msg(MSGL_WARN, + MSGTR_LIBASS_NeitherPlayResXNorPlayResYDefined); + track->PlayResX = 384; + track->PlayResY = 288; + } else { + double orig_aspect = + (settings_priv->aspect * render_priv->height * + render_priv->orig_width) / render_priv->orig_height / + render_priv->width; + if (!track->PlayResY && track->PlayResX == 1280) { + track->PlayResY = 1024; + ass_msg(MSGL_WARN, + MSGTR_LIBASS_PlayResYUndefinedSettingY, track->PlayResY); + } else if (!track->PlayResY) { + track->PlayResY = track->PlayResX / orig_aspect + .5; + ass_msg(MSGL_WARN, + MSGTR_LIBASS_PlayResYUndefinedSettingY, track->PlayResY); + } else if (!track->PlayResX && track->PlayResY == 1024) { + track->PlayResX = 1280; + ass_msg(MSGL_WARN, + MSGTR_LIBASS_PlayResXUndefinedSettingX, track->PlayResX); + } else if (!track->PlayResX) { + track->PlayResX = track->PlayResY * orig_aspect + .5; + ass_msg(MSGL_WARN, + MSGTR_LIBASS_PlayResXUndefinedSettingX, track->PlayResX); + } + } } -ass_renderer_t* ass_renderer_init(ass_library_t* library) +ass_renderer_t *ass_renderer_init(ass_library_t *library) { - int error; - FT_Library ft; - ass_renderer_t* priv = 0; - int vmajor, vminor, vpatch; - - memset(&render_context, 0, sizeof(render_context)); - memset(&frame_context, 0, sizeof(frame_context)); - memset(&text_info, 0, sizeof(text_info)); + int error; + FT_Library ft; + ass_renderer_t *priv = 0; + int vmajor, vminor, vpatch; - error = FT_Init_FreeType( &ft ); - if ( error ) { - mp_msg(MSGT_ASS, MSGL_FATAL, MSGTR_LIBASS_FT_Init_FreeTypeFailed); - goto ass_init_exit; - } + error = FT_Init_FreeType(&ft); + if (error) { + ass_msg(MSGL_FATAL, MSGTR_LIBASS_FT_Init_FreeTypeFailed); + goto ass_init_exit; + } - FT_Library_Version(ft, &vmajor, &vminor, &vpatch); - mp_msg(MSGT_ASS, MSGL_V, "FreeType library version: %d.%d.%d\n", - vmajor, vminor, vpatch); - mp_msg(MSGT_ASS, MSGL_V, "FreeType headers version: %d.%d.%d\n", - FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH); + FT_Library_Version(ft, &vmajor, &vminor, &vpatch); + ass_msg(MSGL_V, "FreeType library version: %d.%d.%d\n", + vmajor, vminor, vpatch); + ass_msg(MSGL_V, "FreeType headers version: %d.%d.%d\n", + FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH); - priv = calloc(1, sizeof(ass_renderer_t)); - if (!priv) { - FT_Done_FreeType(ft); - goto ass_init_exit; - } + priv = calloc(1, sizeof(ass_renderer_t)); + if (!priv) { + FT_Done_FreeType(ft); + goto ass_init_exit; + } - priv->synth_priv = ass_synth_init(BLUR_MAX_RADIUS); + priv->synth_priv = ass_synth_init(BLUR_MAX_RADIUS); - priv->library = library; - priv->ftlibrary = ft; - // images_root and related stuff is zero-filled in calloc - - ass_font_cache_init(); - ass_bitmap_cache_init(); - ass_composite_cache_init(); - ass_glyph_cache_init(); + priv->library = library; + priv->ftlibrary = ft; + // images_root and related stuff is zero-filled in calloc - text_info.glyphs = calloc(MAX_GLYPHS, sizeof(glyph_info_t)); - -ass_init_exit: - if (priv) mp_msg(MSGT_ASS, MSGL_INFO, MSGTR_LIBASS_Init); - else mp_msg(MSGT_ASS, MSGL_ERR, MSGTR_LIBASS_InitFailed); + priv->cache.font_cache = ass_font_cache_init(); + priv->cache.bitmap_cache = ass_bitmap_cache_init(); + priv->cache.composite_cache = ass_composite_cache_init(); + priv->cache.glyph_cache = ass_glyph_cache_init(); - return priv; + priv->text_info.max_glyphs = MAX_GLYPHS_INITIAL; + priv->text_info.max_lines = MAX_LINES_INITIAL; + priv->text_info.glyphs = + calloc(MAX_GLYPHS_INITIAL, sizeof(glyph_info_t)); + priv->text_info.lines = calloc(MAX_LINES_INITIAL, sizeof(line_info_t)); + + ass_init_exit: + if (priv) + ass_msg(MSGL_INFO, MSGTR_LIBASS_Init); + else + ass_msg(MSGL_ERR, MSGTR_LIBASS_InitFailed); + + return priv; } -void ass_renderer_done(ass_renderer_t* priv) +void ass_renderer_done(ass_renderer_t *render_priv) { - ass_font_cache_done(); - ass_bitmap_cache_done(); - ass_composite_cache_done(); - ass_glyph_cache_done(); - if (render_context.stroker) { - FT_Stroker_Done(render_context.stroker); - render_context.stroker = 0; - } - if (priv && priv->ftlibrary) FT_Done_FreeType(priv->ftlibrary); - if (priv && priv->fontconfig_priv) fontconfig_done(priv->fontconfig_priv); - if (priv && priv->synth_priv) ass_synth_done(priv->synth_priv); - if (priv && priv->eimg) free(priv->eimg); - if (priv) free(priv); - if (text_info.glyphs) free(text_info.glyphs); + ass_font_cache_done(render_priv->cache.font_cache); + ass_bitmap_cache_done(render_priv->cache.bitmap_cache); + ass_composite_cache_done(render_priv->cache.composite_cache); + ass_glyph_cache_done(render_priv->cache.glyph_cache); + if (render_priv->state.stroker) { + FT_Stroker_Done(render_priv->state.stroker); + render_priv->state.stroker = 0; + } + if (render_priv && render_priv->ftlibrary) + FT_Done_FreeType(render_priv->ftlibrary); + if (render_priv && render_priv->fontconfig_priv) + fontconfig_done(render_priv->fontconfig_priv); + if (render_priv && render_priv->synth_priv) + ass_synth_done(render_priv->synth_priv); + if (render_priv && render_priv->eimg) + free(render_priv->eimg); + free(render_priv); + free(render_priv->text_info.glyphs); + free(render_priv->text_info.lines); + + free(render_priv->settings.default_font); + free(render_priv->settings.default_family); } /** * \brief Create a new ass_image_t * Parameters are the same as ass_image_t fields. */ -static ass_image_t* my_draw_bitmap(unsigned char* bitmap, int bitmap_w, int bitmap_h, int stride, int dst_x, int dst_y, uint32_t color) +static ass_image_t *my_draw_bitmap(unsigned char *bitmap, int bitmap_w, + int bitmap_h, int stride, int dst_x, + int dst_y, uint32_t color) { - ass_image_t* img = calloc(1, sizeof(ass_image_t)); - - img->w = bitmap_w; - img->h = bitmap_h; - img->stride = stride; - img->bitmap = bitmap; - img->color = color; - img->dst_x = dst_x; - img->dst_y = dst_y; + ass_image_t *img = calloc(1, sizeof(ass_image_t)); - return img; + img->w = bitmap_w; + img->h = bitmap_h; + img->stride = stride; + img->bitmap = bitmap; + img->color = color; + img->dst_x = dst_x; + img->dst_y = dst_y; + + return img; +} + +static double x2scr_pos(ass_renderer_t *render_priv, double x); +static double y2scr_pos(ass_renderer_t *render_priv, double y); + +typedef struct rect_s { + int x0; + int y0; + int x1; + int y1; +} rect_t; + +/* + * \brief Convert bitmap glyphs into ass_image_t list with inverse clipping + * + * Inverse clipping with the following strategy: + * - find rectangle from (x0, y0) to (cx0, y1) + * - find rectangle from (cx0, y0) to (cx1, cy0) + * - find rectangle from (cx0, cy1) to (cx1, y1) + * - find rectangle from (cx1, y0) to (x1, y1) + * These rectangles can be invalid and in this case are discarded. + * Afterwards, they are clipped against the screen coordinates. + * In an additional pass, the rectangles need to be split up left/right for + * karaoke effects. This can result in a lot of bitmaps (6 to be exact). + */ +static ass_image_t **render_glyph_i(ass_renderer_t *render_priv, + bitmap_t *bm, int dst_x, int dst_y, + uint32_t color, uint32_t color2, int brk, + ass_image_t **tail) +{ + int i, j, x0, y0, x1, y1, cx0, cy0, cx1, cy1, sx, sy, zx, zy; + rect_t r[4]; + ass_image_t *img; + + dst_x += bm->left; + dst_y += bm->top; + + // we still need to clip against screen boundaries + zx = x2scr_pos(render_priv, 0); + zy = y2scr_pos(render_priv, 0); + sx = x2scr_pos(render_priv, render_priv->track->PlayResX); + sy = y2scr_pos(render_priv, render_priv->track->PlayResY); + + x0 = 0; + y0 = 0; + x1 = bm->w; + y1 = bm->h; + cx0 = render_priv->state.clip_x0 - dst_x; + cy0 = render_priv->state.clip_y0 - dst_y; + cx1 = render_priv->state.clip_x1 - dst_x; + cy1 = render_priv->state.clip_y1 - dst_y; + + // calculate rectangles and discard invalid ones while we're at it. + i = 0; + r[i].x0 = x0; + r[i].y0 = y0; + r[i].x1 = (cx0 > x1) ? x1 : cx0; + r[i].y1 = y1; + if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++; + r[i].x0 = (cx0 < 0) ? x0 : cx0; + r[i].y0 = y0; + r[i].x1 = (cx1 > x1) ? x1 : cx1; + r[i].y1 = (cy0 > y1) ? y1 : cy0; + if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++; + r[i].x0 = (cx0 < 0) ? x0 : cx0; + r[i].y0 = (cy1 < 0) ? y0 : cy1; + r[i].x1 = (cx1 > x1) ? x1 : cx1; + r[i].y1 = y1; + if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++; + r[i].x0 = (cx1 < 0) ? x0 : cx1; + r[i].y0 = y0; + r[i].x1 = x1; + r[i].y1 = y1; + if (r[i].x1 > r[i].x0 && r[i].y1 > r[i].y0) i++; + + // clip each rectangle to screen coordinates + for (j = 0; j < i; j++) { + r[j].x0 = (r[j].x0 + dst_x < zx) ? zx - dst_x : r[j].x0; + r[j].y0 = (r[j].y0 + dst_y < zy) ? zy - dst_y : r[j].y0; + r[j].x1 = (r[j].x1 + dst_x > sx) ? sx - dst_x : r[j].x1; + r[j].y1 = (r[j].y1 + dst_y > sy) ? sy - dst_y : r[j].y1; + } + + // draw the rectangles + for (j = 0; j < i; j++) { + int lbrk = brk; + // kick out rectangles that are invalid now + if (r[j].x1 <= r[j].x0 || r[j].y1 <= r[j].y0) + continue; + // split up into left and right for karaoke, if needed + if (lbrk > r[j].x0) { + if (lbrk > r[j].x1) lbrk = r[j].x1; + img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->w + r[j].x0, + lbrk - r[j].x0, r[j].y1 - r[j].y0, + bm->w, dst_x + r[j].x0, dst_y + r[j].y0, color); + *tail = img; + tail = &img->next; + } + if (lbrk < r[j].x1) { + if (lbrk < r[j].x0) lbrk = r[j].x0; + img = my_draw_bitmap(bm->buffer + r[j].y0 * bm->w + lbrk, + r[j].x1 - lbrk, r[j].y1 - r[j].y0, + bm->w, dst_x + lbrk, dst_y + r[j].y0, color2); + *tail = img; + tail = &img->next; + } + } + + return tail; } /** @@ -340,71 +493,81 @@ static ass_image_t* my_draw_bitmap(unsigned char* bitmap, int bitmap_w, int bitm * \return pointer to the new list tail * Performs clipping. Uses my_draw_bitmap for actual bitmap convertion. */ -static ass_image_t** render_glyph(bitmap_t* bm, int dst_x, int dst_y, uint32_t color, uint32_t color2, int brk, ass_image_t** tail) +static ass_image_t **render_glyph(ass_renderer_t *render_priv, + bitmap_t *bm, int dst_x, int dst_y, + uint32_t color, uint32_t color2, int brk, + ass_image_t **tail) { - // brk is relative to dst_x - // color = color left of brk - // color2 = color right of brk - int b_x0, b_y0, b_x1, b_y1; // visible part of the bitmap - int clip_x0, clip_y0, clip_x1, clip_y1; - int tmp; - ass_image_t* img; + // Inverse clipping in use? + if (render_priv->state.clip_mode) + return render_glyph_i(render_priv, bm, dst_x, dst_y, color, color2, + brk, tail); - dst_x += bm->left; - dst_y += bm->top; - brk -= bm->left; - - // clipping - clip_x0 = render_context.clip_x0; - clip_y0 = render_context.clip_y0; - clip_x1 = render_context.clip_x1; - clip_y1 = render_context.clip_y1; - b_x0 = 0; - b_y0 = 0; - b_x1 = bm->w; - b_y1 = bm->h; - - tmp = dst_x - clip_x0; - if (tmp < 0) { - mp_msg(MSGT_ASS, MSGL_DBG2, "clip left\n"); - b_x0 = - tmp; - } - tmp = dst_y - clip_y0; - if (tmp < 0) { - mp_msg(MSGT_ASS, MSGL_DBG2, "clip top\n"); - b_y0 = - tmp; - } - tmp = clip_x1 - dst_x - bm->w; - if (tmp < 0) { - mp_msg(MSGT_ASS, MSGL_DBG2, "clip right\n"); - b_x1 = bm->w + tmp; - } - tmp = clip_y1 - dst_y - bm->h; - if (tmp < 0) { - mp_msg(MSGT_ASS, MSGL_DBG2, "clip bottom\n"); - b_y1 = bm->h + tmp; - } - - if ((b_y0 >= b_y1) || (b_x0 >= b_x1)) - return tail; + // brk is relative to dst_x + // color = color left of brk + // color2 = color right of brk + int b_x0, b_y0, b_x1, b_y1; // visible part of the bitmap + int clip_x0, clip_y0, clip_x1, clip_y1; + int tmp; + ass_image_t *img; - if (brk > b_x0) { // draw left part - if (brk > b_x1) brk = b_x1; - img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + b_x0, - brk - b_x0, b_y1 - b_y0, bm->w, - dst_x + b_x0, dst_y + b_y0, color); - *tail = img; - tail = &img->next; - } - if (brk < b_x1) { // draw right part - if (brk < b_x0) brk = b_x0; - img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + brk, - b_x1 - brk, b_y1 - b_y0, bm->w, - dst_x + brk, dst_y + b_y0, color2); - *tail = img; - tail = &img->next; - } - return tail; + dst_x += bm->left; + dst_y += bm->top; + brk -= bm->left; + + // clipping + clip_x0 = render_priv->state.clip_x0; + clip_y0 = render_priv->state.clip_y0; + clip_x1 = render_priv->state.clip_x1; + clip_y1 = render_priv->state.clip_y1; + b_x0 = 0; + b_y0 = 0; + b_x1 = bm->w; + b_y1 = bm->h; + + tmp = dst_x - clip_x0; + if (tmp < 0) { + ass_msg(MSGL_DBG2, "clip left\n"); + b_x0 = -tmp; + } + tmp = dst_y - clip_y0; + if (tmp < 0) { + ass_msg(MSGL_DBG2, "clip top\n"); + b_y0 = -tmp; + } + tmp = clip_x1 - dst_x - bm->w; + if (tmp < 0) { + ass_msg(MSGL_DBG2, "clip right\n"); + b_x1 = bm->w + tmp; + } + tmp = clip_y1 - dst_y - bm->h; + if (tmp < 0) { + ass_msg(MSGL_DBG2, "clip bottom\n"); + b_y1 = bm->h + tmp; + } + + if ((b_y0 >= b_y1) || (b_x0 >= b_x1)) + return tail; + + if (brk > b_x0) { // draw left part + if (brk > b_x1) + brk = b_x1; + img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + b_x0, + brk - b_x0, b_y1 - b_y0, bm->w, + dst_x + b_x0, dst_y + b_y0, color); + *tail = img; + tail = &img->next; + } + if (brk < b_x1) { // draw right part + if (brk < b_x0) + brk = b_x0; + img = my_draw_bitmap(bm->buffer + bm->w * b_y0 + brk, + b_x1 - brk, b_y1 - b_y0, bm->w, + dst_x + brk, dst_y + b_y0, color2); + *tail = img; + tail = &img->next; + } + return tail; } /** @@ -413,331 +576,381 @@ static ass_image_t** render_glyph(bitmap_t* bm, int dst_x, int dst_y, uint32_t c * Mainly useful for translucent glyphs and especially borders, to avoid the * luminance adding up where they overlap (which looks ugly) */ -static void render_overlap(ass_image_t** last_tail, ass_image_t** tail, bitmap_hash_key_t *last_hash, bitmap_hash_key_t* hash) { - int left, top, bottom, right; - int old_left, old_top, w, h, cur_left, cur_top; - int x, y, opos, cpos; - char m; - composite_hash_key_t hk; - composite_hash_val_t *hv; - composite_hash_key_t *nhk; - int ax = (*last_tail)->dst_x; - int ay = (*last_tail)->dst_y; - int aw = (*last_tail)->w; - int as = (*last_tail)->stride; - int ah = (*last_tail)->h; - int bx = (*tail)->dst_x; - int by = (*tail)->dst_y; - int bw = (*tail)->w; - int bs = (*tail)->stride; - int bh = (*tail)->h; - unsigned char* a; - unsigned char* b; +static void +render_overlap(ass_renderer_t *render_priv, ass_image_t **last_tail, + ass_image_t **tail, bitmap_hash_key_t *last_hash, + bitmap_hash_key_t *hash) +{ + int left, top, bottom, right; + int old_left, old_top, w, h, cur_left, cur_top; + int x, y, opos, cpos; + char m; + composite_hash_key_t hk; + composite_hash_val_t *hv; + composite_hash_val_t chv; + int ax = (*last_tail)->dst_x; + int ay = (*last_tail)->dst_y; + int aw = (*last_tail)->w; + int as = (*last_tail)->stride; + int ah = (*last_tail)->h; + int bx = (*tail)->dst_x; + int by = (*tail)->dst_y; + int bw = (*tail)->w; + int bs = (*tail)->stride; + int bh = (*tail)->h; + unsigned char *a; + unsigned char *b; - if ((*last_tail)->bitmap == (*tail)->bitmap) - return; + if ((*last_tail)->bitmap == (*tail)->bitmap) + return; - if ((*last_tail)->color != (*tail)->color) - return; + if ((*last_tail)->color != (*tail)->color) + return; - // Calculate overlap coordinates - left = (ax > bx) ? ax : bx; - top = (ay > by) ? ay : by; - right = ((ax+aw) < (bx+bw)) ? (ax+aw) : (bx+bw); - bottom = ((ay+ah) < (by+bh)) ? (ay+ah) : (by+bh); - if ((right <= left) || (bottom <= top)) - return; - old_left = left-ax; - old_top = top-ay; - w = right-left; - h = bottom-top; - cur_left = left-bx; - cur_top = top-by; + // Calculate overlap coordinates + left = (ax > bx) ? ax : bx; + top = (ay > by) ? ay : by; + right = ((ax + aw) < (bx + bw)) ? (ax + aw) : (bx + bw); + bottom = ((ay + ah) < (by + bh)) ? (ay + ah) : (by + bh); + if ((right <= left) || (bottom <= top)) + return; + old_left = left - ax; + old_top = top - ay; + w = right - left; + h = bottom - top; + cur_left = left - bx; + cur_top = top - by; - // Query cache - memset(&hk, 0, sizeof(hk)); - memcpy(&hk.a, last_hash, sizeof(*last_hash)); - memcpy(&hk.b, hash, sizeof(*hash)); - hk.aw = aw; - hk.ah = ah; - hk.bw = bw; - hk.bh = bh; - hk.ax = ax; - hk.ay = ay; - hk.bx = bx; - hk.by = by; - hv = cache_find_composite(&hk); - if (hv) { - (*last_tail)->bitmap = hv->a; - (*tail)->bitmap = hv->b; - return; - } + // Query cache + memset(&hk, 0, sizeof(hk)); + memcpy(&hk.a, last_hash, sizeof(*last_hash)); + memcpy(&hk.b, hash, sizeof(*hash)); + hk.aw = aw; + hk.ah = ah; + hk.bw = bw; + hk.bh = bh; + hk.ax = ax; + hk.ay = ay; + hk.bx = bx; + hk.by = by; + hv = cache_find_composite(render_priv->cache.composite_cache, &hk); + if (hv) { + (*last_tail)->bitmap = hv->a; + (*tail)->bitmap = hv->b; + return; + } + // Allocate new bitmaps and copy over data + a = (*last_tail)->bitmap; + b = (*tail)->bitmap; + (*last_tail)->bitmap = malloc(as * ah); + (*tail)->bitmap = malloc(bs * bh); + memcpy((*last_tail)->bitmap, a, as * ah); + memcpy((*tail)->bitmap, b, bs * bh); - // Allocate new bitmaps and copy over data - a = (*last_tail)->bitmap; - b = (*tail)->bitmap; - (*last_tail)->bitmap = malloc(as*ah); - (*tail)->bitmap = malloc(bs*bh); - memcpy((*last_tail)->bitmap, a, as*ah); - memcpy((*tail)->bitmap, b, bs*bh); + // Composite overlapping area + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) { + opos = (old_top + y) * (as) + (old_left + x); + cpos = (cur_top + y) * (bs) + (cur_left + x); + m = (a[opos] > b[cpos]) ? a[opos] : b[cpos]; + (*last_tail)->bitmap[opos] = 0; + (*tail)->bitmap[cpos] = m; + } - // Composite overlapping area - for (y=0; y b[cpos]) ? a[opos] : b[cpos]; - (*last_tail)->bitmap[opos] = 0; - (*tail)->bitmap[cpos] = m; - } - - // Insert bitmaps into the cache - nhk = calloc(1, sizeof(*nhk)); - memcpy(nhk, &hk, sizeof(*nhk)); - hv = calloc(1, sizeof(*hv)); - hv->a = (*last_tail)->bitmap; - hv->b = (*tail)->bitmap; - cache_add_composite(nhk, hv); + // Insert bitmaps into the cache + chv.a = (*last_tail)->bitmap; + chv.b = (*tail)->bitmap; + cache_add_composite(render_priv->cache.composite_cache, &hk, &chv); } /** * \brief Convert text_info_t struct to ass_image_t list * Splits glyphs in halves when needed (for \kf karaoke). */ -static ass_image_t* render_text(text_info_t* text_info, int dst_x, int dst_y) +static ass_image_t *render_text(ass_renderer_t *render_priv, int dst_x, + int dst_y) { - int pen_x, pen_y; - int i; - bitmap_t* bm; - ass_image_t* head; - ass_image_t** tail = &head; - ass_image_t** last_tail = 0; - ass_image_t** here_tail = 0; - bitmap_hash_key_t* last_hash = 0; + int pen_x, pen_y; + int i; + bitmap_t *bm; + ass_image_t *head; + ass_image_t **tail = &head; + ass_image_t **last_tail = 0; + ass_image_t **here_tail = 0; + bitmap_hash_key_t *last_hash = 0; + text_info_t *text_info = &render_priv->text_info; - for (i = 0; i < text_info->length; ++i) { - glyph_info_t* info = text_info->glyphs + i; - if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_s || (info->shadow == 0)) - continue; + for (i = 0; i < text_info->length; ++i) { + glyph_info_t *info = text_info->glyphs + i; + if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_s + || (info->shadow_x == 0 && info->shadow_y == 0)) + continue; - pen_x = dst_x + info->pos.x + ROUND(info->shadow * frame_context.border_scale); - pen_y = dst_y + info->pos.y + ROUND(info->shadow * frame_context.border_scale); - bm = info->bm_s; + pen_x = + dst_x + (info->pos.x >> 6) + + (int) (info->shadow_x * render_priv->border_scale); + pen_y = + dst_y + (info->pos.y >> 6) + + (int) (info->shadow_y * render_priv->border_scale); + bm = info->bm_s; - here_tail = tail; - tail = render_glyph(bm, pen_x, pen_y, info->c[3], 0, 1000000, tail); - if (last_tail && tail != here_tail && ((info->c[3] & 0xff) > 0)) - render_overlap(last_tail, here_tail, last_hash, &info->hash_key); - last_tail = here_tail; - last_hash = &info->hash_key; - } + here_tail = tail; + tail = + render_glyph(render_priv, bm, pen_x, pen_y, info->c[3], 0, + 1000000, tail); + if (last_tail && tail != here_tail && ((info->c[3] & 0xff) > 0)) + render_overlap(render_priv, last_tail, here_tail, last_hash, + &info->hash_key); + last_tail = here_tail; + last_hash = &info->hash_key; + } - last_tail = 0; - for (i = 0; i < text_info->length; ++i) { - glyph_info_t* info = text_info->glyphs + i; - if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_o) - continue; + last_tail = 0; + for (i = 0; i < text_info->length; ++i) { + glyph_info_t *info = text_info->glyphs + i; + if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_o) + continue; - pen_x = dst_x + info->pos.x; - pen_y = dst_y + info->pos.y; - bm = info->bm_o; - - if ((info->effect_type == EF_KARAOKE_KO) && (info->effect_timing <= info->bbox.xMax)) { - // do nothing - } else { - here_tail = tail; - tail = render_glyph(bm, pen_x, pen_y, info->c[2], 0, 1000000, tail); - if (last_tail && tail != here_tail && ((info->c[2] & 0xff) > 0)) - render_overlap(last_tail, here_tail, last_hash, &info->hash_key); - last_tail = here_tail; - last_hash = &info->hash_key; - } - } - for (i = 0; i < text_info->length; ++i) { - glyph_info_t* info = text_info->glyphs + i; - if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm) - continue; + pen_x = dst_x + (info->pos.x >> 6); + pen_y = dst_y + (info->pos.y >> 6); + bm = info->bm_o; - pen_x = dst_x + info->pos.x; - pen_y = dst_y + info->pos.y; - bm = info->bm; + if ((info->effect_type == EF_KARAOKE_KO) + && (info->effect_timing <= info->bbox.xMax)) { + // do nothing + } else { + here_tail = tail; + tail = + render_glyph(render_priv, bm, pen_x, pen_y, info->c[2], + 0, 1000000, tail); + if (last_tail && tail != here_tail && ((info->c[2] & 0xff) > 0)) + render_overlap(render_priv, last_tail, here_tail, + last_hash, &info->hash_key); + last_tail = here_tail; + last_hash = &info->hash_key; + } + } + for (i = 0; i < text_info->length; ++i) { + glyph_info_t *info = text_info->glyphs + i; + if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm) + continue; - if ((info->effect_type == EF_KARAOKE) || (info->effect_type == EF_KARAOKE_KO)) { - if (info->effect_timing > info->bbox.xMax) - tail = render_glyph(bm, pen_x, pen_y, info->c[0], 0, 1000000, tail); - else - tail = render_glyph(bm, pen_x, pen_y, info->c[1], 0, 1000000, tail); - } else if (info->effect_type == EF_KARAOKE_KF) { - tail = render_glyph(bm, pen_x, pen_y, info->c[0], info->c[1], info->effect_timing, tail); - } else - tail = render_glyph(bm, pen_x, pen_y, info->c[0], 0, 1000000, tail); - } + pen_x = dst_x + (info->pos.x >> 6); + pen_y = dst_y + (info->pos.y >> 6); + bm = info->bm; - *tail = 0; - return head; + if ((info->effect_type == EF_KARAOKE) + || (info->effect_type == EF_KARAOKE_KO)) { + if (info->effect_timing > info->bbox.xMax) + tail = + render_glyph(render_priv, bm, pen_x, pen_y, + info->c[0], 0, 1000000, tail); + else + tail = + render_glyph(render_priv, bm, pen_x, pen_y, + info->c[1], 0, 1000000, tail); + } else if (info->effect_type == EF_KARAOKE_KF) { + tail = + render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], + info->c[1], info->effect_timing, tail); + } else + tail = + render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], + 0, 1000000, tail); + } + + *tail = 0; + return head; } /** * \brief Mapping between script and screen coordinates */ -static int x2scr(double x) { - return x*frame_context.orig_width_nocrop / frame_context.track->PlayResX + - FFMAX(global_settings->left_margin, 0); +static double x2scr(ass_renderer_t *render_priv, double x) +{ + return x * render_priv->orig_width_nocrop / + render_priv->track->PlayResX + + FFMAX(render_priv->settings.left_margin, 0); } -static double x2scr_pos(double x) { - return x*frame_context.orig_width / frame_context.track->PlayResX + - global_settings->left_margin; +static double x2scr_pos(ass_renderer_t *render_priv, double x) +{ + return x * render_priv->orig_width / render_priv->track->PlayResX + + render_priv->settings.left_margin; } + /** * \brief Mapping between script and screen coordinates */ -static double y2scr(double y) { - return y * frame_context.orig_height_nocrop / frame_context.track->PlayResY + - FFMAX(global_settings->top_margin, 0); +static double y2scr(ass_renderer_t *render_priv, double y) +{ + return y * render_priv->orig_height_nocrop / + render_priv->track->PlayResY + + FFMAX(render_priv->settings.top_margin, 0); } -static double y2scr_pos(double y) { - return y * frame_context.orig_height / frame_context.track->PlayResY + - global_settings->top_margin; +static double y2scr_pos(ass_renderer_t *render_priv, double y) +{ + return y * render_priv->orig_height / render_priv->track->PlayResY + + render_priv->settings.top_margin; } // the same for toptitles -static int y2scr_top(double y) { - if (global_settings->use_margins) - return y * frame_context.orig_height_nocrop / frame_context.track->PlayResY; - else - return y * frame_context.orig_height_nocrop / frame_context.track->PlayResY + - FFMAX(global_settings->top_margin, 0); +static double y2scr_top(ass_renderer_t *render_priv, double y) +{ + if (render_priv->settings.use_margins) + return y * render_priv->orig_height_nocrop / + render_priv->track->PlayResY; + else + return y * render_priv->orig_height_nocrop / + render_priv->track->PlayResY + + FFMAX(render_priv->settings.top_margin, 0); } + // the same for subtitles -static int y2scr_sub(double y) { - if (global_settings->use_margins) - return y * frame_context.orig_height_nocrop / frame_context.track->PlayResY + - FFMAX(global_settings->top_margin, 0) + - FFMAX(global_settings->bottom_margin, 0); - else - return y * frame_context.orig_height_nocrop / frame_context.track->PlayResY + - FFMAX(global_settings->top_margin, 0); +static double y2scr_sub(ass_renderer_t *render_priv, double y) +{ + if (render_priv->settings.use_margins) + return y * render_priv->orig_height_nocrop / + render_priv->track->PlayResY + + FFMAX(render_priv->settings.top_margin, + 0) + FFMAX(render_priv->settings.bottom_margin, 0); + else + return y * render_priv->orig_height_nocrop / + render_priv->track->PlayResY + + FFMAX(render_priv->settings.top_margin, 0); } -static void compute_string_bbox( text_info_t* info, FT_BBox *abbox ) { - FT_BBox bbox; - int i; - - if (text_info.length > 0) { - bbox.xMin = 32000; - bbox.xMax = -32000; - bbox.yMin = - d6_to_int(text_info.lines[0].asc) + text_info.glyphs[0].pos.y; - bbox.yMax = d6_to_int(text_info.height - text_info.lines[0].asc) + text_info.glyphs[0].pos.y; +static void compute_string_bbox(text_info_t *info, double_bbox_t *bbox) +{ + int i; - for (i = 0; i < text_info.length; ++i) { - int s = text_info.glyphs[i].pos.x; - int e = s + d6_to_int(text_info.glyphs[i].advance.x); - bbox.xMin = FFMIN(bbox.xMin, s); - bbox.xMax = FFMAX(bbox.xMax, e); - } - } else - bbox.xMin = bbox.xMax = bbox.yMin = bbox.yMax = 0; + if (info->length > 0) { + bbox->xMin = 32000; + bbox->xMax = -32000; + bbox->yMin = -1 * info->lines[0].asc + d6_to_double(info->glyphs[0].pos.y); + bbox->yMax = info->height - info->lines[0].asc + + d6_to_double(info->glyphs[0].pos.y); - /* return string bbox */ - *abbox = bbox; + for (i = 0; i < info->length; ++i) { + double s = d6_to_double(info->glyphs[i].pos.x); + double e = s + d6_to_double(info->glyphs[i].advance.x); + bbox->xMin = FFMIN(bbox->xMin, s); + bbox->xMax = FFMAX(bbox->xMax, e); + } + } else + bbox->xMin = bbox->xMax = bbox->yMin = bbox->yMax = 0.; } /** * \brief Check if starting part of (*p) matches sample. If true, shift p to the first symbol after the matching part. */ -static inline int mystrcmp(char** p, const char* sample) { - int len = strlen(sample); - if (strncmp(*p, sample, len) == 0) { - (*p) += len; - return 1; - } else - return 0; +static inline int mystrcmp(char **p, const char *sample) +{ + int len = strlen(sample); + if (strncmp(*p, sample, len) == 0) { + (*p) += len; + return 1; + } else + return 0; } -static void change_font_size(double sz) +static void change_font_size(ass_renderer_t *render_priv, double sz) { - double size = sz * frame_context.font_scale; + double size = sz * render_priv->font_scale; - if (size < 1) - size = 1; - else if (size > frame_context.height * 2) - size = frame_context.height * 2; + if (size < 1) + size = 1; + else if (size > render_priv->height * 2) + size = render_priv->height * 2; - ass_font_set_size(render_context.font, size); + ass_font_set_size(render_priv->state.font, size); - render_context.font_size = sz; + render_priv->state.font_size = sz; } /** - * \brief Change current font, using setting from render_context. + * \brief Change current font, using setting from render_priv->state. */ -static void update_font(void) +static void update_font(ass_renderer_t *render_priv) { - unsigned val; - ass_renderer_t* priv = frame_context.ass_priv; - ass_font_desc_t desc; - desc.family = strdup(render_context.family); - desc.treat_family_as_pattern = render_context.treat_family_as_pattern; + unsigned val; + ass_font_desc_t desc; + desc.family = strdup(render_priv->state.family); + desc.treat_family_as_pattern = + render_priv->state.treat_family_as_pattern; - val = render_context.bold; - // 0 = normal, 1 = bold, >1 = exact weight - if (val == 0) val = 80; // normal - else if (val == 1) val = 200; // bold - desc.bold = val; + val = render_priv->state.bold; + // 0 = normal, 1 = bold, >1 = exact weight + if (val == 0) + val = 80; // normal + else if (val == 1) + val = 200; // bold + desc.bold = val; - val = render_context.italic; - if (val == 0) val = 0; // normal - else if (val == 1) val = 110; //italic - desc.italic = val; + val = render_priv->state.italic; + if (val == 0) + val = 0; // normal + else if (val == 1) + val = 110; //italic + desc.italic = val; - render_context.font = ass_font_new(priv->library, priv->ftlibrary, priv->fontconfig_priv, &desc); - free(desc.family); - - if (render_context.font) - change_font_size(render_context.font_size); + render_priv->state.font = + ass_font_new(render_priv->cache.font_cache, render_priv->library, + render_priv->ftlibrary, render_priv->fontconfig_priv, + &desc); + free(desc.family); + + if (render_priv->state.font) + change_font_size(render_priv, render_priv->state.font_size); } /** * \brief Change border width * negative value resets border to style value */ -static void change_border(double border) +static void change_border(ass_renderer_t *render_priv, double border_x, + double border_y) { - int b; - if (!render_context.font) return; + int bord; + if (!render_priv->state.font) + return; - if (border < 0) { - if (render_context.style->BorderStyle == 1) - border = render_context.style->Outline; - else - border = 1.; - } - render_context.border = border; + if (border_x < 0 && border_y < 0) { + if (render_priv->state.style->BorderStyle == 1) + border_x = border_y = render_priv->state.style->Outline; + else + border_x = border_y = 1.; + } - b = 64 * border * frame_context.border_scale; - if (b > 0) { - if (!render_context.stroker) { - int error; + render_priv->state.border_x = border_x; + render_priv->state.border_y = border_y; + + bord = 64 * border_x * render_priv->border_scale; + if (bord > 0 && border_x == border_y) { + if (!render_priv->state.stroker) { + int error; #if (FREETYPE_MAJOR > 2) || ((FREETYPE_MAJOR == 2) && (FREETYPE_MINOR > 1)) - error = FT_Stroker_New( ass_renderer->ftlibrary, &render_context.stroker ); -#else // < 2.2 - error = FT_Stroker_New( render_context.font->faces[0]->memory, &render_context.stroker ); + error = + FT_Stroker_New(render_priv->ftlibrary, + &render_priv->state.stroker); +#else // < 2.2 + error = + FT_Stroker_New(render_priv->state.font->faces[0]-> + memory, &render_priv->state.stroker); #endif - if (error) { - mp_msg(MSGT_ASS, MSGL_V, "failed to get stroker\n"); - render_context.stroker = 0; - } - } - if (render_context.stroker) - FT_Stroker_Set( render_context.stroker, b, - FT_STROKER_LINECAP_ROUND, - FT_STROKER_LINEJOIN_ROUND, - 0 ); - } else { - FT_Stroker_Done(render_context.stroker); - render_context.stroker = 0; - } + if (error) { + ass_msg(MSGL_V, "failed to get stroker\n"); + render_priv->state.stroker = 0; + } + } + if (render_priv->state.stroker) + FT_Stroker_Set(render_priv->state.stroker, bord, + FT_STROKER_LINECAP_ROUND, + FT_STROKER_LINEJOIN_ROUND, 0); + } else { + FT_Stroker_Done(render_priv->state.stroker); + render_priv->state.stroker = 0; + } } #define _r(c) ((c)>>24) @@ -749,18 +962,19 @@ static void change_border(double border) * \brief Calculate a weighted average of two colors * calculates c1*(1-a) + c2*a, but separately for each component except alpha */ -static void change_color(uint32_t* var, uint32_t new, double pwr) +static void change_color(uint32_t *var, uint32_t new, double pwr) { - (*var)= ((uint32_t)(_r(*var) * (1 - pwr) + _r(new) * pwr) << 24) + - ((uint32_t)(_g(*var) * (1 - pwr) + _g(new) * pwr) << 16) + - ((uint32_t)(_b(*var) * (1 - pwr) + _b(new) * pwr) << 8) + - _a(*var); + (*var) = ((uint32_t) (_r(*var) * (1 - pwr) + _r(new) * pwr) << 24) + + ((uint32_t) (_g(*var) * (1 - pwr) + _g(new) * pwr) << 16) + + ((uint32_t) (_b(*var) * (1 - pwr) + _b(new) * pwr) << 8) + _a(*var); } // like change_color, but for alpha component only -static void change_alpha(uint32_t* var, uint32_t new, double pwr) +static void change_alpha(uint32_t *var, uint32_t new, double pwr) { - *var = (_r(*var) << 24) + (_g(*var) << 16) + (_b(*var) << 8) + (_a(*var) * (1 - pwr) + _a(new) * pwr); + *var = + (_r(*var) << 24) + (_g(*var) << 16) + (_b(*var) << 8) + + (_a(*var) * (1 - pwr) + _a(new) * pwr); } /** @@ -772,466 +986,573 @@ static void change_alpha(uint32_t* var, uint32_t new, double pwr) */ static uint32_t mult_alpha(uint32_t a, uint32_t b) { - return 0xFF - (0xFF - a) * (0xFF - b) / 0xFF; + return 0xFF - (0xFF - a) * (0xFF - b) / 0xFF; } /** * \brief Calculate alpha value by piecewise linear function * Used for \fad, \fade implementation. */ -static unsigned interpolate_alpha(long long now, - long long t1, long long t2, long long t3, long long t4, - unsigned a1, unsigned a2, unsigned a3) +static unsigned +interpolate_alpha(long long now, + long long t1, long long t2, long long t3, long long t4, + unsigned a1, unsigned a2, unsigned a3) { - unsigned a; - double cf; - if (now <= t1) { - a = a1; - } else if (now >= t4) { - a = a3; - } else if (now < t2) { // and > t1 - cf = ((double)(now - t1)) / (t2 - t1); - a = a1 * (1 - cf) + a2 * cf; - } else if (now > t3) { - cf = ((double)(now - t3)) / (t4 - t3); - a = a2 * (1 - cf) + a3 * cf; - } else { // t2 <= now <= t3 - a = a2; - } + unsigned a; + double cf; + if (now <= t1) { + a = a1; + } else if (now >= t4) { + a = a3; + } else if (now < t2) { // and > t1 + cf = ((double) (now - t1)) / (t2 - t1); + a = a1 * (1 - cf) + a2 * cf; + } else if (now > t3) { + cf = ((double) (now - t3)) / (t4 - t3); + a = a2 * (1 - cf) + a3 * cf; + } else { // t2 <= now <= t3 + a = a2; + } - return a; + return a; } -static void reset_render_context(void); +static void reset_render_context(ass_renderer_t *); /** * \brief Parse style override tag. * \param p string to parse * \param pwr multiplier for some tag effects (comes from \t tags) */ -static char* parse_tag(char* p, double pwr) { +static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr) +{ #define skip_to(x) while ((*p != (x)) && (*p != '}') && (*p != 0)) { ++p;} #define skip(x) if (*p == (x)) ++p; else { return p; } - - skip_to('\\'); - skip('\\'); - if ((*p == '}') || (*p == 0)) - return p; - // New tags introduced in vsfilter 2.39 - if (mystrcmp(&p, "xbord")) { - double val; - if (mystrtod(&p, &val)) - mp_msg(MSGT_ASS, MSGL_V, "stub: \\xbord%.2f\n", val); - } else if (mystrcmp(&p, "ybord")) { - double val; - if (mystrtod(&p, &val)) - mp_msg(MSGT_ASS, MSGL_V, "stub: \\ybord%.2f\n", val); - } else if (mystrcmp(&p, "xshad")) { - int val; - if (mystrtoi(&p, &val)) - mp_msg(MSGT_ASS, MSGL_V, "stub: \\xshad%d\n", val); - } else if (mystrcmp(&p, "yshad")) { - int val; - if (mystrtoi(&p, &val)) - mp_msg(MSGT_ASS, MSGL_V, "stub: \\yshad%d\n", val); - } else if (mystrcmp(&p, "fax")) { - int val; - if (mystrtoi(&p, &val)) - mp_msg(MSGT_ASS, MSGL_V, "stub: \\fax%d\n", val); - } else if (mystrcmp(&p, "fay")) { - int val; - if (mystrtoi(&p, &val)) - mp_msg(MSGT_ASS, MSGL_V, "stub: \\fay%d\n", val); - } else if (mystrcmp(&p, "iclip")) { - int x0, y0, x1, y1; - int res = 1; - skip('('); - res &= mystrtoi(&p, &x0); - skip(','); - res &= mystrtoi(&p, &y0); - skip(','); - res &= mystrtoi(&p, &x1); - skip(','); - res &= mystrtoi(&p, &y1); - skip(')'); - mp_msg(MSGT_ASS, MSGL_V, "stub: \\iclip(%d,%d,%d,%d)\n", x0, y0, x1, y1); - } else if (mystrcmp(&p, "blur")) { - double val; - if (mystrtod(&p, &val)) { - val = (val < 0) ? 0 : val; - val = (val > BLUR_MAX_RADIUS) ? BLUR_MAX_RADIUS : val; - render_context.blur = val; - } else - render_context.blur = 0.0; - // ASS standard tags - } else if (mystrcmp(&p, "fsc")) { - char tp = *p++; - double val; - if (tp == 'x') { - if (mystrtod(&p, &val)) { - val /= 100; - render_context.scale_x = render_context.scale_x * ( 1 - pwr) + val * pwr; - } else - render_context.scale_x = render_context.style->ScaleX; - } else if (tp == 'y') { - if (mystrtod(&p, &val)) { - val /= 100; - render_context.scale_y = render_context.scale_y * ( 1 - pwr) + val * pwr; - } else - render_context.scale_y = render_context.style->ScaleY; - } - } else if (mystrcmp(&p, "fsp")) { - double val; - if (mystrtod(&p, &val)) - render_context.hspacing = render_context.hspacing * ( 1 - pwr ) + val * pwr; - else - render_context.hspacing = render_context.style->Spacing; - } else if (mystrcmp(&p, "fs")) { - double val; - if (mystrtod(&p, &val)) - val = render_context.font_size * ( 1 - pwr ) + val * pwr; - else - val = render_context.style->FontSize; - if (render_context.font) - change_font_size(val); - } else if (mystrcmp(&p, "bord")) { - double val; - if (mystrtod(&p, &val)) - val = render_context.border * ( 1 - pwr ) + val * pwr; - else - val = -1.; // reset to default - change_border(val); - } else if (mystrcmp(&p, "move")) { - double x1, x2, y1, y2; - long long t1, t2, delta_t, t; - double x, y; - double k; - skip('('); - mystrtod(&p, &x1); - skip(','); - mystrtod(&p, &y1); - skip(','); - mystrtod(&p, &x2); - skip(','); - mystrtod(&p, &y2); - if (*p == ',') { - skip(','); - mystrtoll(&p, &t1); - skip(','); - mystrtoll(&p, &t2); - mp_msg(MSGT_ASS, MSGL_DBG2, "movement6: (%f, %f) -> (%f, %f), (%" PRId64 " .. %" PRId64 ")\n", - x1, y1, x2, y2, (int64_t)t1, (int64_t)t2); - } else { - t1 = 0; - t2 = render_context.event->Duration; - mp_msg(MSGT_ASS, MSGL_DBG2, "movement: (%f, %f) -> (%f, %f)\n", x1, y1, x2, y2); - } - skip(')'); - delta_t = t2 - t1; - t = frame_context.time - render_context.event->Start; - if (t < t1) - k = 0.; - else if (t > t2) - k = 1.; - else k = ((double)(t - t1)) / delta_t; - x = k * (x2 - x1) + x1; - y = k * (y2 - y1) + y1; - if (render_context.evt_type != EVENT_POSITIONED) { - render_context.pos_x = x; - render_context.pos_y = y; - render_context.detect_collisions = 0; - render_context.evt_type = EVENT_POSITIONED; - } - } else if (mystrcmp(&p, "frx")) { - double val; - if (mystrtod(&p, &val)) { - val *= M_PI / 180; - render_context.frx = val * pwr + render_context.frx * (1-pwr); - } else - render_context.frx = 0.; - } else if (mystrcmp(&p, "fry")) { - double val; - if (mystrtod(&p, &val)) { - val *= M_PI / 180; - render_context.fry = val * pwr + render_context.fry * (1-pwr); - } else - render_context.fry = 0.; - } else if (mystrcmp(&p, "frz") || mystrcmp(&p, "fr")) { - double val; - if (mystrtod(&p, &val)) { - val *= M_PI / 180; - render_context.frz = val * pwr + render_context.frz * (1-pwr); - } else - render_context.frz = M_PI * render_context.style->Angle / 180.; - } else if (mystrcmp(&p, "fn")) { - char* start = p; - char* family; - skip_to('\\'); - if (p > start) { - family = malloc(p - start + 1); - strncpy(family, start, p - start); - family[p - start] = '\0'; - } else - family = strdup(render_context.style->FontName); - if (render_context.family) - free(render_context.family); - render_context.family = family; - update_font(); - } else if (mystrcmp(&p, "alpha")) { - uint32_t val; - int i; - if (strtocolor(&p, &val)) { - unsigned char a = val >> 24; - for (i = 0; i < 4; ++i) - change_alpha(&render_context.c[i], a, pwr); - } else { - change_alpha(&render_context.c[0], render_context.style->PrimaryColour, pwr); - change_alpha(&render_context.c[1], render_context.style->SecondaryColour, pwr); - change_alpha(&render_context.c[2], render_context.style->OutlineColour, pwr); - change_alpha(&render_context.c[3], render_context.style->BackColour, pwr); - } - // FIXME: simplify - } else if (mystrcmp(&p, "an")) { - int val; - if (mystrtoi(&p, &val) && val) { - int v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment - mp_msg(MSGT_ASS, MSGL_DBG2, "an %d\n", val); - if (v != 0) v = 3 - v; - val = ((val - 1) % 3) + 1; // horizontal alignment - val += v*4; - mp_msg(MSGT_ASS, MSGL_DBG2, "align %d\n", val); - render_context.alignment = val; - } else - render_context.alignment = render_context.style->Alignment; - } else if (mystrcmp(&p, "a")) { - int val; - if (mystrtoi(&p, &val) && val) - render_context.alignment = val; - else - render_context.alignment = render_context.style->Alignment; - } else if (mystrcmp(&p, "pos")) { - double v1, v2; - skip('('); - mystrtod(&p, &v1); - skip(','); - mystrtod(&p, &v2); - skip(')'); - mp_msg(MSGT_ASS, MSGL_DBG2, "pos(%f, %f)\n", v1, v2); - if (render_context.evt_type == EVENT_POSITIONED) { - mp_msg(MSGT_ASS, MSGL_V, "Subtitle has a new \\pos " - "after \\move or \\pos, ignoring\n"); - } else { - render_context.evt_type = EVENT_POSITIONED; - render_context.detect_collisions = 0; - render_context.pos_x = v1; - render_context.pos_y = v2; - } - } else if (mystrcmp(&p, "fad")) { - int a1, a2, a3; - long long t1, t2, t3, t4; - if (*p == 'e') ++p; // either \fad or \fade - skip('('); - mystrtoi(&p, &a1); - skip(','); - mystrtoi(&p, &a2); - if (*p == ')') { - // 2-argument version (\fad, according to specs) - // a1 and a2 are fade-in and fade-out durations - t1 = 0; - t4 = render_context.event->Duration; - t2 = a1; - t3 = t4 - a2; - a1 = 0xFF; - a2 = 0; - a3 = 0xFF; - } else { - // 6-argument version (\fade) - // a1 and a2 (and a3) are opacity values - skip(','); - mystrtoi(&p, &a3); - skip(','); - mystrtoll(&p, &t1); - skip(','); - mystrtoll(&p, &t2); - skip(','); - mystrtoll(&p, &t3); - skip(','); - mystrtoll(&p, &t4); - } - skip(')'); - render_context.fade = interpolate_alpha(frame_context.time - render_context.event->Start, t1, t2, t3, t4, a1, a2, a3); - } else if (mystrcmp(&p, "org")) { - int v1, v2; - skip('('); - mystrtoi(&p, &v1); - skip(','); - mystrtoi(&p, &v2); - skip(')'); - mp_msg(MSGT_ASS, MSGL_DBG2, "org(%d, %d)\n", v1, v2); - // render_context.evt_type = EVENT_POSITIONED; - if (!render_context.have_origin) { - render_context.org_x = v1; - render_context.org_y = v2; - render_context.have_origin = 1; - render_context.detect_collisions = 0; - } - } else if (mystrcmp(&p, "t")) { - double v[3]; - int v1, v2; - double v3; - int cnt; - long long t1, t2, t, delta_t; - double k; - skip('('); - for (cnt = 0; cnt < 3; ++cnt) { - if (*p == '\\') - break; - v[cnt] = strtod(p, &p); - skip(','); - } - if (cnt == 3) { - v1 = v[0]; v2 = v[1]; v3 = v[2]; - } else if (cnt == 2) { - v1 = v[0]; v2 = v[1]; v3 = 1.; - } else if (cnt == 1) { - v1 = 0; v2 = render_context.event->Duration; v3 = v[0]; - } else { // cnt == 0 - v1 = 0; v2 = render_context.event->Duration; v3 = 1.; - } - render_context.detect_collisions = 0; - t1 = v1; - t2 = v2; - delta_t = v2 - v1; - if (v3 < 0.) - v3 = 0.; - t = frame_context.time - render_context.event->Start; // FIXME: move to render_context - if (t <= t1) - k = 0.; - else if (t >= t2) - k = 1.; - else { - assert(delta_t != 0.); - k = pow(((double)(t - t1)) / delta_t, v3); - } - while (*p == '\\') - p = parse_tag(p, k); // maybe k*pwr ? no, specs forbid nested \t's - skip_to(')'); // in case there is some unknown tag or a comment - skip(')'); - } else if (mystrcmp(&p, "clip")) { - int x0, y0, x1, y1; - int res = 1; - skip('('); - res &= mystrtoi(&p, &x0); - skip(','); - res &= mystrtoi(&p, &y0); - skip(','); - res &= mystrtoi(&p, &x1); - skip(','); - res &= mystrtoi(&p, &y1); - skip(')'); - if (res) { - render_context.clip_x0 = render_context.clip_x0 * (1-pwr) + x0 * pwr; - render_context.clip_x1 = render_context.clip_x1 * (1-pwr) + x1 * pwr; - render_context.clip_y0 = render_context.clip_y0 * (1-pwr) + y0 * pwr; - render_context.clip_y1 = render_context.clip_y1 * (1-pwr) + y1 * pwr; - } else { - render_context.clip_x0 = 0; - render_context.clip_y0 = 0; - render_context.clip_x1 = frame_context.track->PlayResX; - render_context.clip_y1 = frame_context.track->PlayResY; - } - } else if (mystrcmp(&p, "c")) { - uint32_t val; - if (!strtocolor(&p, &val)) - val = render_context.style->PrimaryColour; - mp_msg(MSGT_ASS, MSGL_DBG2, "color: %X\n", val); - change_color(&render_context.c[0], val, pwr); - } else if ((*p >= '1') && (*p <= '4') && (++p) && (mystrcmp(&p, "c") || mystrcmp(&p, "a"))) { - char n = *(p-2); - int cidx = n - '1'; - char cmd = *(p-1); - uint32_t val; - assert((n >= '1') && (n <= '4')); - if (!strtocolor(&p, &val)) - switch(n) { - case '1': val = render_context.style->PrimaryColour; break; - case '2': val = render_context.style->SecondaryColour; break; - case '3': val = render_context.style->OutlineColour; break; - case '4': val = render_context.style->BackColour; break; - default : val = 0; break; // impossible due to assert; avoid compilation warning - } - switch (cmd) { - case 'c': change_color(render_context.c + cidx, val, pwr); break; - case 'a': change_alpha(render_context.c + cidx, val >> 24, pwr); break; - default: mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_BadCommand, n, cmd); break; - } - mp_msg(MSGT_ASS, MSGL_DBG2, "single c/a at %f: %c%c = %X \n", pwr, n, cmd, render_context.c[cidx]); - } else if (mystrcmp(&p, "r")) { - reset_render_context(); - } else if (mystrcmp(&p, "be")) { - int val; - if (mystrtoi(&p, &val)) { - // Clamp to a safe upper limit, since high values need excessive CPU - val = (val < 0) ? 0 : val; - val = (val > MAX_BE) ? MAX_BE : val; - render_context.be = val; - } else - render_context.be = 0; - } else if (mystrcmp(&p, "b")) { - int b; - if (mystrtoi(&p, &b)) { - if (pwr >= .5) - render_context.bold = b; - } else - render_context.bold = render_context.style->Bold; - update_font(); - } else if (mystrcmp(&p, "i")) { - int i; - if (mystrtoi(&p, &i)) { - if (pwr >= .5) - render_context.italic = i; - } else - render_context.italic = render_context.style->Italic; - update_font(); - } else if (mystrcmp(&p, "kf") || mystrcmp(&p, "K")) { - int val = 0; - mystrtoi(&p, &val); - render_context.effect_type = EF_KARAOKE_KF; - if (render_context.effect_timing) - render_context.effect_skip_timing += render_context.effect_timing; - render_context.effect_timing = val * 10; - } else if (mystrcmp(&p, "ko")) { - int val = 0; - mystrtoi(&p, &val); - render_context.effect_type = EF_KARAOKE_KO; - if (render_context.effect_timing) - render_context.effect_skip_timing += render_context.effect_timing; - render_context.effect_timing = val * 10; - } else if (mystrcmp(&p, "k")) { - int val = 0; - mystrtoi(&p, &val); - render_context.effect_type = EF_KARAOKE; - if (render_context.effect_timing) - render_context.effect_skip_timing += render_context.effect_timing; - render_context.effect_timing = val * 10; - } else if (mystrcmp(&p, "shad")) { - int val; - if (mystrtoi(&p, &val)) - render_context.shadow = val; - else - render_context.shadow = render_context.style->Shadow; - } else if (mystrcmp(&p, "pbo")) { - int val = 0; - mystrtoi(&p, &val); // ignored - } else if (mystrcmp(&p, "p")) { - int val; - if (!mystrtoi(&p, &val)) - val = 0; - render_context.drawing_mode = !!val; - } + skip_to('\\'); + skip('\\'); + if ((*p == '}') || (*p == 0)) + return p; - return p; + // New tags introduced in vsfilter 2.39 + if (mystrcmp(&p, "xbord")) { + double val; + if (mystrtod(&p, &val)) + val = render_priv->state.border_x * (1 - pwr) + val * pwr; + else + val = -1.; + change_border(render_priv, val, render_priv->state.border_y); + } else if (mystrcmp(&p, "ybord")) { + double val; + if (mystrtod(&p, &val)) + val = render_priv->state.border_y * (1 - pwr) + val * pwr; + else + val = -1.; + change_border(render_priv, render_priv->state.border_x, val); + } else if (mystrcmp(&p, "xshad")) { + double val; + if (mystrtod(&p, &val)) + val = render_priv->state.shadow_x * (1 - pwr) + val * pwr; + else + val = 0.; + render_priv->state.shadow_x = val; + } else if (mystrcmp(&p, "yshad")) { + double val; + if (mystrtod(&p, &val)) + val = render_priv->state.shadow_y * (1 - pwr) + val * pwr; + else + val = 0.; + render_priv->state.shadow_y = val; + } else if (mystrcmp(&p, "fax")) { + double val; + if (mystrtod(&p, &val)) + render_priv->state.fax = + val * pwr + render_priv->state.fax * (1 - pwr); + else + render_priv->state.fax = 0.; + } else if (mystrcmp(&p, "fay")) { + double val; + if (mystrtod(&p, &val)) + render_priv->state.fay = + val * pwr + render_priv->state.fay * (1 - pwr); + else + render_priv->state.fay = 0.; + } else if (mystrcmp(&p, "iclip")) { + int x0, y0, x1, y1; + int res = 1; + skip('('); + res &= mystrtoi(&p, &x0); + skip(','); + res &= mystrtoi(&p, &y0); + skip(','); + res &= mystrtoi(&p, &x1); + skip(','); + res &= mystrtoi(&p, &y1); + skip(')'); + if (res) { + render_priv->state.clip_x0 = + render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr; + render_priv->state.clip_x1 = + render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr; + render_priv->state.clip_y0 = + render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr; + render_priv->state.clip_y1 = + render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr; + render_priv->state.clip_mode = 1; + } else + render_priv->state.clip_mode = 0; + } else if (mystrcmp(&p, "blur")) { + double val; + if (mystrtod(&p, &val)) { + val = render_priv->state.blur * (1 - pwr) + val * pwr; + val = (val < 0) ? 0 : val; + val = (val > BLUR_MAX_RADIUS) ? BLUR_MAX_RADIUS : val; + render_priv->state.blur = val; + } else + render_priv->state.blur = 0.0; + // ASS standard tags + } else if (mystrcmp(&p, "fsc")) { + char tp = *p++; + double val; + if (tp == 'x') { + if (mystrtod(&p, &val)) { + val /= 100; + render_priv->state.scale_x = + render_priv->state.scale_x * (1 - pwr) + val * pwr; + } else + render_priv->state.scale_x = + render_priv->state.style->ScaleX; + } else if (tp == 'y') { + if (mystrtod(&p, &val)) { + val /= 100; + render_priv->state.scale_y = + render_priv->state.scale_y * (1 - pwr) + val * pwr; + } else + render_priv->state.scale_y = + render_priv->state.style->ScaleY; + } + } else if (mystrcmp(&p, "fsp")) { + double val; + if (mystrtod(&p, &val)) + render_priv->state.hspacing = + render_priv->state.hspacing * (1 - pwr) + val * pwr; + else + render_priv->state.hspacing = render_priv->state.style->Spacing; + } else if (mystrcmp(&p, "fs")) { + double val; + if (mystrtod(&p, &val)) + val = render_priv->state.font_size * (1 - pwr) + val * pwr; + else + val = render_priv->state.style->FontSize; + if (render_priv->state.font) + change_font_size(render_priv, val); + } else if (mystrcmp(&p, "bord")) { + double val; + if (mystrtod(&p, &val)) { + if (render_priv->state.border_x == render_priv->state.border_y) + val = render_priv->state.border_x * (1 - pwr) + val * pwr; + } else + val = -1.; // reset to default + change_border(render_priv, val, val); + } else if (mystrcmp(&p, "move")) { + double x1, x2, y1, y2; + long long t1, t2, delta_t, t; + double x, y; + double k; + skip('('); + mystrtod(&p, &x1); + skip(','); + mystrtod(&p, &y1); + skip(','); + mystrtod(&p, &x2); + skip(','); + mystrtod(&p, &y2); + if (*p == ',') { + skip(','); + mystrtoll(&p, &t1); + skip(','); + mystrtoll(&p, &t2); + ass_msg(MSGL_DBG2, + "movement6: (%f, %f) -> (%f, %f), (%" PRId64 " .. %" + PRId64 ")\n", x1, y1, x2, y2, (int64_t) t1, + (int64_t) t2); + } else { + t1 = 0; + t2 = render_priv->state.event->Duration; + ass_msg(MSGL_DBG2, + "movement: (%f, %f) -> (%f, %f)\n", x1, y1, x2, y2); + } + skip(')'); + delta_t = t2 - t1; + t = render_priv->time - render_priv->state.event->Start; + if (t < t1) + k = 0.; + else if (t > t2) + k = 1.; + else + k = ((double) (t - t1)) / delta_t; + x = k * (x2 - x1) + x1; + y = k * (y2 - y1) + y1; + if (render_priv->state.evt_type != EVENT_POSITIONED) { + render_priv->state.pos_x = x; + render_priv->state.pos_y = y; + render_priv->state.detect_collisions = 0; + render_priv->state.evt_type = EVENT_POSITIONED; + } + } else if (mystrcmp(&p, "frx")) { + double val; + if (mystrtod(&p, &val)) { + val *= M_PI / 180; + render_priv->state.frx = + val * pwr + render_priv->state.frx * (1 - pwr); + } else + render_priv->state.frx = 0.; + } else if (mystrcmp(&p, "fry")) { + double val; + if (mystrtod(&p, &val)) { + val *= M_PI / 180; + render_priv->state.fry = + val * pwr + render_priv->state.fry * (1 - pwr); + } else + render_priv->state.fry = 0.; + } else if (mystrcmp(&p, "frz") || mystrcmp(&p, "fr")) { + double val; + if (mystrtod(&p, &val)) { + val *= M_PI / 180; + render_priv->state.frz = + val * pwr + render_priv->state.frz * (1 - pwr); + } else + render_priv->state.frz = + M_PI * render_priv->state.style->Angle / 180.; + } else if (mystrcmp(&p, "fn")) { + char *start = p; + char *family; + skip_to('\\'); + if (p > start) { + family = malloc(p - start + 1); + strncpy(family, start, p - start); + family[p - start] = '\0'; + } else + family = strdup(render_priv->state.style->FontName); + if (render_priv->state.family) + free(render_priv->state.family); + render_priv->state.family = family; + update_font(render_priv); + } else if (mystrcmp(&p, "alpha")) { + uint32_t val; + int i; + if (strtocolor(&p, &val)) { + unsigned char a = val >> 24; + for (i = 0; i < 4; ++i) + change_alpha(&render_priv->state.c[i], a, pwr); + } else { + change_alpha(&render_priv->state.c[0], + render_priv->state.style->PrimaryColour, pwr); + change_alpha(&render_priv->state.c[1], + render_priv->state.style->SecondaryColour, pwr); + change_alpha(&render_priv->state.c[2], + render_priv->state.style->OutlineColour, pwr); + change_alpha(&render_priv->state.c[3], + render_priv->state.style->BackColour, pwr); + } + // FIXME: simplify + } else if (mystrcmp(&p, "an")) { + int val; + if (mystrtoi(&p, &val) && val) { + int v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment + ass_msg(MSGL_DBG2, "an %d\n", val); + if (v != 0) + v = 3 - v; + val = ((val - 1) % 3) + 1; // horizontal alignment + val += v * 4; + ass_msg(MSGL_DBG2, "align %d\n", val); + render_priv->state.alignment = val; + } else + render_priv->state.alignment = + render_priv->state.style->Alignment; + } else if (mystrcmp(&p, "a")) { + int val; + if (mystrtoi(&p, &val) && val) + render_priv->state.alignment = val; + else + render_priv->state.alignment = + render_priv->state.style->Alignment; + } else if (mystrcmp(&p, "pos")) { + double v1, v2; + skip('('); + mystrtod(&p, &v1); + skip(','); + mystrtod(&p, &v2); + skip(')'); + ass_msg(MSGL_DBG2, "pos(%f, %f)\n", v1, v2); + if (render_priv->state.evt_type == EVENT_POSITIONED) { + ass_msg(MSGL_V, "Subtitle has a new \\pos " + "after \\move or \\pos, ignoring\n"); + } else { + render_priv->state.evt_type = EVENT_POSITIONED; + render_priv->state.detect_collisions = 0; + render_priv->state.pos_x = v1; + render_priv->state.pos_y = v2; + } + } else if (mystrcmp(&p, "fad")) { + int a1, a2, a3; + long long t1, t2, t3, t4; + if (*p == 'e') + ++p; // either \fad or \fade + skip('('); + mystrtoi(&p, &a1); + skip(','); + mystrtoi(&p, &a2); + if (*p == ')') { + // 2-argument version (\fad, according to specs) + // a1 and a2 are fade-in and fade-out durations + t1 = 0; + t4 = render_priv->state.event->Duration; + t2 = a1; + t3 = t4 - a2; + a1 = 0xFF; + a2 = 0; + a3 = 0xFF; + } else { + // 6-argument version (\fade) + // a1 and a2 (and a3) are opacity values + skip(','); + mystrtoi(&p, &a3); + skip(','); + mystrtoll(&p, &t1); + skip(','); + mystrtoll(&p, &t2); + skip(','); + mystrtoll(&p, &t3); + skip(','); + mystrtoll(&p, &t4); + } + skip(')'); + render_priv->state.fade = + interpolate_alpha(render_priv->time - + render_priv->state.event->Start, t1, t2, + t3, t4, a1, a2, a3); + } else if (mystrcmp(&p, "org")) { + int v1, v2; + skip('('); + mystrtoi(&p, &v1); + skip(','); + mystrtoi(&p, &v2); + skip(')'); + ass_msg(MSGL_DBG2, "org(%d, %d)\n", v1, v2); + // render_priv->state.evt_type = EVENT_POSITIONED; + if (!render_priv->state.have_origin) { + render_priv->state.org_x = v1; + render_priv->state.org_y = v2; + render_priv->state.have_origin = 1; + render_priv->state.detect_collisions = 0; + } + } else if (mystrcmp(&p, "t")) { + double v[3]; + int v1, v2; + double v3; + int cnt; + long long t1, t2, t, delta_t; + double k; + skip('('); + for (cnt = 0; cnt < 3; ++cnt) { + if (*p == '\\') + break; + v[cnt] = strtod(p, &p); + skip(','); + } + if (cnt == 3) { + v1 = v[0]; + v2 = (v[1] <= v1) ? render_priv->state.event->Duration : v[1]; + v3 = v[2]; + } else if (cnt == 2) { + v1 = v[0]; + v2 = (v[1] <= v1) ? render_priv->state.event->Duration : v[1]; + v3 = 1.; + } else if (cnt == 1) { + v1 = 0; + v2 = render_priv->state.event->Duration; + v3 = v[0]; + } else { // cnt == 0 + v1 = 0; + v2 = render_priv->state.event->Duration; + v3 = 1.; + } + render_priv->state.detect_collisions = 0; + t1 = v1; + t2 = v2; + delta_t = v2 - v1; + if (v3 < 0.) + v3 = 0.; + t = render_priv->time - render_priv->state.event->Start; // FIXME: move to render_context + if (t <= t1) + k = 0.; + else if (t >= t2) + k = 1.; + else { + assert(delta_t != 0.); + k = pow(((double) (t - t1)) / delta_t, v3); + } + while (*p == '\\') + p = parse_tag(render_priv, p, k); // maybe k*pwr ? no, specs forbid nested \t's + skip_to(')'); // in case there is some unknown tag or a comment + skip(')'); + } else if (mystrcmp(&p, "clip")) { + int x0, y0, x1, y1; + int res = 1; + skip('('); + res &= mystrtoi(&p, &x0); + skip(','); + res &= mystrtoi(&p, &y0); + skip(','); + res &= mystrtoi(&p, &x1); + skip(','); + res &= mystrtoi(&p, &y1); + skip(')'); + if (res) { + render_priv->state.clip_x0 = + render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr; + render_priv->state.clip_x1 = + render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr; + render_priv->state.clip_y0 = + render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr; + render_priv->state.clip_y1 = + render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr; + } else { + render_priv->state.clip_x0 = 0; + render_priv->state.clip_y0 = 0; + render_priv->state.clip_x1 = render_priv->track->PlayResX; + render_priv->state.clip_y1 = render_priv->track->PlayResY; + } + } else if (mystrcmp(&p, "c")) { + uint32_t val; + if (!strtocolor(&p, &val)) + val = render_priv->state.style->PrimaryColour; + ass_msg(MSGL_DBG2, "color: %X\n", val); + change_color(&render_priv->state.c[0], val, pwr); + } else if ((*p >= '1') && (*p <= '4') && (++p) + && (mystrcmp(&p, "c") || mystrcmp(&p, "a"))) { + char n = *(p - 2); + int cidx = n - '1'; + char cmd = *(p - 1); + uint32_t val; + assert((n >= '1') && (n <= '4')); + if (!strtocolor(&p, &val)) + switch (n) { + case '1': + val = render_priv->state.style->PrimaryColour; + break; + case '2': + val = render_priv->state.style->SecondaryColour; + break; + case '3': + val = render_priv->state.style->OutlineColour; + break; + case '4': + val = render_priv->state.style->BackColour; + break; + default: + val = 0; + break; // impossible due to assert; avoid compilation warning + } + switch (cmd) { + case 'c': + change_color(render_priv->state.c + cidx, val, pwr); + break; + case 'a': + change_alpha(render_priv->state.c + cidx, val >> 24, pwr); + break; + default: + ass_msg(MSGL_WARN, MSGTR_LIBASS_BadCommand, n, cmd); + break; + } + ass_msg(MSGL_DBG2, "single c/a at %f: %c%c = %X \n", + pwr, n, cmd, render_priv->state.c[cidx]); + } else if (mystrcmp(&p, "r")) { + reset_render_context(render_priv); + } else if (mystrcmp(&p, "be")) { + int val; + if (mystrtoi(&p, &val)) { + // Clamp to a safe upper limit, since high values need excessive CPU + val = (val < 0) ? 0 : val; + val = (val > MAX_BE) ? MAX_BE : val; + render_priv->state.be = val; + } else + render_priv->state.be = 0; + } else if (mystrcmp(&p, "b")) { + int b; + if (mystrtoi(&p, &b)) { + if (pwr >= .5) + render_priv->state.bold = b; + } else + render_priv->state.bold = render_priv->state.style->Bold; + update_font(render_priv); + } else if (mystrcmp(&p, "i")) { + int i; + if (mystrtoi(&p, &i)) { + if (pwr >= .5) + render_priv->state.italic = i; + } else + render_priv->state.italic = render_priv->state.style->Italic; + update_font(render_priv); + } else if (mystrcmp(&p, "kf") || mystrcmp(&p, "K")) { + int val = 0; + mystrtoi(&p, &val); + render_priv->state.effect_type = EF_KARAOKE_KF; + if (render_priv->state.effect_timing) + render_priv->state.effect_skip_timing += + render_priv->state.effect_timing; + render_priv->state.effect_timing = val * 10; + } else if (mystrcmp(&p, "ko")) { + int val = 0; + mystrtoi(&p, &val); + render_priv->state.effect_type = EF_KARAOKE_KO; + if (render_priv->state.effect_timing) + render_priv->state.effect_skip_timing += + render_priv->state.effect_timing; + render_priv->state.effect_timing = val * 10; + } else if (mystrcmp(&p, "k")) { + int val = 0; + mystrtoi(&p, &val); + render_priv->state.effect_type = EF_KARAOKE; + if (render_priv->state.effect_timing) + render_priv->state.effect_skip_timing += + render_priv->state.effect_timing; + render_priv->state.effect_timing = val * 10; + } else if (mystrcmp(&p, "shad")) { + double val; + if (mystrtod(&p, &val)) { + if (render_priv->state.shadow_x == render_priv->state.shadow_y) + val = render_priv->state.shadow_x * (1 - pwr) + val * pwr; + } else + val = 0.; + render_priv->state.shadow_x = render_priv->state.shadow_y = val; + } else if (mystrcmp(&p, "s")) { + int val; + if (mystrtoi(&p, &val) && val) + render_priv->state.flags |= DECO_STRIKETHROUGH; + else + render_priv->state.flags &= ~DECO_STRIKETHROUGH; + } else if (mystrcmp(&p, "u")) { + int val; + if (mystrtoi(&p, &val) && val) + render_priv->state.flags |= DECO_UNDERLINE; + else + render_priv->state.flags &= ~DECO_UNDERLINE; + } else if (mystrcmp(&p, "pbo")) { + double val = 0; + if (mystrtod(&p, &val)) + render_priv->state.drawing->pbo = val; + } else if (mystrcmp(&p, "p")) { + int val; + if (!mystrtoi(&p, &val)) + val = 0; + if (val) + render_priv->state.drawing->scale = val; + render_priv->state.drawing_mode = !!val; + } + + return p; #undef skip #undef skip_to @@ -1243,110 +1564,123 @@ static char* parse_tag(char* p, double pwr) { * \return ucs4 code of the next char * On return str points to the unparsed part of the string */ -static unsigned get_next_char(char** str) +static unsigned get_next_char(ass_renderer_t *render_priv, char **str) { - char* p = *str; - unsigned chr; - if (*p == '{') { // '\0' goes here - p++; - while (1) { - p = parse_tag(p, 1.); - if (*p == '}') { // end of tag - p++; - if (*p == '{') { - p++; - continue; - } else - break; - } else if (*p != '\\') - mp_msg(MSGT_ASS, MSGL_V, "Unable to parse: \"%s\" \n", p); - if (*p == 0) - break; - } - } - if (*p == '\t') { - ++p; - *str = p; - return ' '; - } - if (*p == '\\') { - if ((*(p+1) == 'N') || ((*(p+1) == 'n') && (frame_context.track->WrapStyle == 2))) { - p += 2; - *str = p; - return '\n'; - } else if ((*(p+1) == 'n') || (*(p+1) == 'h')) { - p += 2; - *str = p; - return ' '; - } - } - chr = utf8_get_char((const char **)&p); - *str = p; - return chr; + char *p = *str; + unsigned chr; + if (*p == '{') { // '\0' goes here + p++; + while (1) { + p = parse_tag(render_priv, p, 1.); + if (*p == '}') { // end of tag + p++; + if (*p == '{') { + p++; + continue; + } else + break; + } else if (*p != '\\') + ass_msg(MSGL_V, "Unable to parse: \"%s\" \n", p); + if (*p == 0) + break; + } + } + if (*p == '\t') { + ++p; + *str = p; + return ' '; + } + if (*p == '\\') { + if ((*(p + 1) == 'N') + || ((*(p + 1) == 'n') + && (render_priv->track->WrapStyle == 2))) { + p += 2; + *str = p; + return '\n'; + } else if ((*(p + 1) == 'n') || (*(p + 1) == 'h')) { + p += 2; + *str = p; + return ' '; + } + } + chr = ass_utf8_get_char((char **) &p); + *str = p; + return chr; } -static void apply_transition_effects(ass_event_t* event) +static void +apply_transition_effects(ass_renderer_t *render_priv, ass_event_t *event) { - int v[4]; - int cnt; - char* p = event->Effect; + int v[4]; + int cnt; + char *p = event->Effect; - if (!p || !*p) return; + if (!p || !*p) + return; - cnt = 0; - while (cnt < 4 && (p = strchr(p, ';'))) { - v[cnt++] = atoi(++p); - } - - if (strncmp(event->Effect, "Banner;", 7) == 0) { - int delay; - if (cnt < 1) { - mp_msg(MSGT_ASS, MSGL_V, "Error parsing effect: %s \n", event->Effect); - return; - } - if (cnt >= 2 && v[1] == 0) // right-to-left - render_context.scroll_direction = SCROLL_RL; - else // left-to-right - render_context.scroll_direction = SCROLL_LR; + cnt = 0; + while (cnt < 4 && (p = strchr(p, ';'))) { + v[cnt++] = atoi(++p); + } - delay = v[0]; - if (delay == 0) delay = 1; // ? - render_context.scroll_shift = (frame_context.time - render_context.event->Start) / delay; - render_context.evt_type = EVENT_HSCROLL; - return; - } + if (strncmp(event->Effect, "Banner;", 7) == 0) { + int delay; + if (cnt < 1) { + ass_msg(MSGL_V, "Error parsing effect: %s \n", + event->Effect); + return; + } + if (cnt >= 2 && v[1] == 0) // right-to-left + render_priv->state.scroll_direction = SCROLL_RL; + else // left-to-right + render_priv->state.scroll_direction = SCROLL_LR; - if (strncmp(event->Effect, "Scroll up;", 10) == 0) { - render_context.scroll_direction = SCROLL_BT; - } else if (strncmp(event->Effect, "Scroll down;", 12) == 0) { - render_context.scroll_direction = SCROLL_TB; - } else { - mp_msg(MSGT_ASS, MSGL_V, "Unknown transition effect: %s \n", event->Effect); - return; - } - // parse scroll up/down parameters - { - int delay; - int y0, y1; - if (cnt < 3) { - mp_msg(MSGT_ASS, MSGL_V, "Error parsing effect: %s \n", event->Effect); - return; - } - delay = v[2]; - if (delay == 0) delay = 1; // ? - render_context.scroll_shift = (frame_context.time - render_context.event->Start) / delay; - if (v[0] < v[1]) { - y0 = v[0]; y1 = v[1]; - } else { - y0 = v[1]; y1 = v[0]; - } - if (y1 == 0) - y1 = frame_context.track->PlayResY; // y0=y1=0 means fullscreen scrolling - render_context.clip_y0 = y0; - render_context.clip_y1 = y1; - render_context.evt_type = EVENT_VSCROLL; - render_context.detect_collisions = 0; - } + delay = v[0]; + if (delay == 0) + delay = 1; // ? + render_priv->state.scroll_shift = + (render_priv->time - render_priv->state.event->Start) / delay; + render_priv->state.evt_type = EVENT_HSCROLL; + return; + } + + if (strncmp(event->Effect, "Scroll up;", 10) == 0) { + render_priv->state.scroll_direction = SCROLL_BT; + } else if (strncmp(event->Effect, "Scroll down;", 12) == 0) { + render_priv->state.scroll_direction = SCROLL_TB; + } else { + ass_msg(MSGL_V, "Unknown transition effect: %s \n", + event->Effect); + return; + } + // parse scroll up/down parameters + { + int delay; + int y0, y1; + if (cnt < 3) { + ass_msg(MSGL_V, "Error parsing effect: %s \n", + event->Effect); + return; + } + delay = v[2]; + if (delay == 0) + delay = 1; // ? + render_priv->state.scroll_shift = + (render_priv->time - render_priv->state.event->Start) / delay; + if (v[0] < v[1]) { + y0 = v[0]; + y1 = v[1]; + } else { + y0 = v[1]; + y1 = v[0]; + } + if (y1 == 0) + y1 = render_priv->track->PlayResY; // y0=y1=0 means fullscreen scrolling + render_priv->state.clip_y0 = y0; + render_priv->state.clip_y1 = y1; + render_priv->state.evt_type = EVENT_VSCROLL; + render_priv->state.detect_collisions = 0; + } } @@ -1354,68 +1688,205 @@ static void apply_transition_effects(ass_event_t* event) * \brief partially reset render_context to style values * Works like {\r}: resets some style overrides */ -static void reset_render_context(void) +static void reset_render_context(ass_renderer_t *render_priv) { - render_context.c[0] = render_context.style->PrimaryColour; - render_context.c[1] = render_context.style->SecondaryColour; - render_context.c[2] = render_context.style->OutlineColour; - render_context.c[3] = render_context.style->BackColour; - render_context.font_size = render_context.style->FontSize; + render_priv->state.c[0] = render_priv->state.style->PrimaryColour; + render_priv->state.c[1] = render_priv->state.style->SecondaryColour; + render_priv->state.c[2] = render_priv->state.style->OutlineColour; + render_priv->state.c[3] = render_priv->state.style->BackColour; + render_priv->state.flags = + (render_priv->state.style->Underline ? DECO_UNDERLINE : 0) | + (render_priv->state.style->StrikeOut ? DECO_STRIKETHROUGH : 0); + render_priv->state.font_size = render_priv->state.style->FontSize; - if (render_context.family) - free(render_context.family); - render_context.family = strdup(render_context.style->FontName); - render_context.treat_family_as_pattern = render_context.style->treat_fontname_as_pattern; - render_context.bold = render_context.style->Bold; - render_context.italic = render_context.style->Italic; - update_font(); + if (render_priv->state.family) + free(render_priv->state.family); + render_priv->state.family = strdup(render_priv->state.style->FontName); + render_priv->state.treat_family_as_pattern = + render_priv->state.style->treat_fontname_as_pattern; + render_priv->state.bold = render_priv->state.style->Bold; + render_priv->state.italic = render_priv->state.style->Italic; + update_font(render_priv); - change_border(-1.); - render_context.scale_x = render_context.style->ScaleX; - render_context.scale_y = render_context.style->ScaleY; - render_context.hspacing = render_context.style->Spacing; - render_context.be = 0; - render_context.blur = 0.0; - render_context.shadow = render_context.style->Shadow; - render_context.frx = render_context.fry = 0.; - render_context.frz = M_PI * render_context.style->Angle / 180.; + change_border(render_priv, -1., -1.); + render_priv->state.scale_x = render_priv->state.style->ScaleX; + render_priv->state.scale_y = render_priv->state.style->ScaleY; + render_priv->state.hspacing = render_priv->state.style->Spacing; + render_priv->state.be = 0; + render_priv->state.blur = 0.0; + render_priv->state.shadow_x = render_priv->state.style->Shadow; + render_priv->state.shadow_y = render_priv->state.style->Shadow; + render_priv->state.frx = render_priv->state.fry = 0.; + render_priv->state.frz = M_PI * render_priv->state.style->Angle / 180.; + render_priv->state.fax = render_priv->state.fay = 0.; - // FIXME: does not reset unsupported attributes. + // FIXME: does not reset unsupported attributes. } /** - * \brief Start new event. Reset render_context. + * \brief Start new event. Reset render_priv->state. */ -static void init_render_context(ass_event_t* event) +static void +init_render_context(ass_renderer_t *render_priv, ass_event_t *event) { - render_context.event = event; - render_context.style = frame_context.track->styles + event->Style; + render_priv->state.event = event; + render_priv->state.style = render_priv->track->styles + event->Style; - reset_render_context(); + reset_render_context(render_priv); - render_context.evt_type = EVENT_NORMAL; - render_context.alignment = render_context.style->Alignment; - render_context.pos_x = 0; - render_context.pos_y = 0; - render_context.org_x = 0; - render_context.org_y = 0; - render_context.have_origin = 0; - render_context.clip_x0 = 0; - render_context.clip_y0 = 0; - render_context.clip_x1 = frame_context.track->PlayResX; - render_context.clip_y1 = frame_context.track->PlayResY; - render_context.detect_collisions = 1; - render_context.fade = 0; - render_context.drawing_mode = 0; - render_context.effect_type = EF_NONE; - render_context.effect_timing = 0; - render_context.effect_skip_timing = 0; - - apply_transition_effects(event); + render_priv->state.evt_type = EVENT_NORMAL; + render_priv->state.alignment = render_priv->state.style->Alignment; + render_priv->state.pos_x = 0; + render_priv->state.pos_y = 0; + render_priv->state.org_x = 0; + render_priv->state.org_y = 0; + render_priv->state.have_origin = 0; + render_priv->state.clip_x0 = 0; + render_priv->state.clip_y0 = 0; + render_priv->state.clip_x1 = render_priv->track->PlayResX; + render_priv->state.clip_y1 = render_priv->track->PlayResY; + render_priv->state.detect_collisions = 1; + render_priv->state.fade = 0; + render_priv->state.drawing_mode = 0; + render_priv->state.effect_type = EF_NONE; + render_priv->state.effect_timing = 0; + render_priv->state.effect_skip_timing = 0; + render_priv->state.drawing = + ass_drawing_new(render_priv->fontconfig_priv, + render_priv->state.font, + render_priv->settings.hinting, + render_priv->ftlibrary); + + apply_transition_effects(render_priv, event); } -static void free_render_context(void) +static void free_render_context(ass_renderer_t *render_priv) { + ass_drawing_free(render_priv->state.drawing); +} + +// Calculate the cbox of a series of points +static void +get_contour_cbox(FT_BBox *box, FT_Vector *points, int start, int end) +{ + box->xMin = box->yMin = INT_MAX; + box->xMax = box->yMax = INT_MIN; + int i; + + for (i = start; i < end; i++) { + box->xMin = (points[i].x < box->xMin) ? points[i].x : box->xMin; + box->xMax = (points[i].x > box->xMax) ? points[i].x : box->xMax; + box->yMin = (points[i].y < box->yMin) ? points[i].y : box->yMin; + box->yMax = (points[i].y > box->yMax) ? points[i].y : box->yMax; + } +} + +/** + * \brief Fix-up stroker result for huge borders by removing the contours from + * the outline that are harmful. +*/ +static void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, + int border_y) +{ + int nc = glyph->outline.n_contours; + int begin, stop; + char modified = 0; + char *valid_cont; + int start = 0; + int end = -1; + FT_BBox *boxes = calloc(nc, sizeof(FT_BBox)); + int i, j; + + // Create a list of cboxes of the contours + for (i = 0; i < nc; i++) { + start = end + 1; + end = glyph->outline.contours[i]; + get_contour_cbox(&boxes[i], glyph->outline.points, start, end); + } + + // if a) contour's cbox is contained in another contours cbox + // b) contour's height or width is smaller than the border*2 + // the contour can be safely removed. + valid_cont = calloc(1, nc); + for (i = 0; i < nc; i++) { + valid_cont[i] = 1; + for (j = 0; j < nc; j++) { + if (i == j) + continue; + if (boxes[i].xMin >= boxes[j].xMin && + boxes[i].xMax <= boxes[j].xMax && + boxes[i].yMin >= boxes[j].yMin && + boxes[i].yMax <= boxes[j].yMax) { + int width = boxes[i].xMax - boxes[i].xMin; + int height = boxes[i].yMax - boxes[i].yMin; + if (width < border_x * 2 || height < border_y * 2) { + valid_cont[i] = 0; + modified = 1; + break; + } + } + } + } + + // Zero-out contours that can be removed; much simpler than copying + if (modified) { + for (i = 0; i < nc; i++) { + if (valid_cont[i]) + continue; + begin = (i == 0) ? 0 : glyph->outline.contours[i - 1] + 1; + stop = glyph->outline.contours[i]; + for (j = begin; j <= stop; j++) { + glyph->outline.points[j].x = 0; + glyph->outline.points[j].y = 0; + glyph->outline.tags[j] = 0; + } + } + } + + free(boxes); + free(valid_cont); +} + +/* + * Stroke an outline glyph in x/y direction. Applies various fixups to get + * around limitations of the FreeType stroker. + */ +static void stroke_outline_glyph(ass_renderer_t *render_priv, + FT_OutlineGlyph *glyph, int sx, int sy) +{ + if (sx <= 0 || sy <= 0) + return; + + fix_freetype_stroker(*glyph, sx, sy); + + // Borders are equal; use the regular stroker + if (sx == sy && render_priv->state.stroker) { + int error; + error = FT_Glyph_StrokeBorder((FT_Glyph *) glyph, + render_priv->state.stroker, 0, 1); + if (error) + ass_msg(MSGL_WARN, MSGTR_LIBASS_FT_Glyph_Stroke_Error, error); + + // "Stroke" with the outline emboldener in two passes. + // The outlines look uglier, but the emboldening never adds any points + } else { + int i; + FT_Outline *ol = &(*glyph)->outline; + FT_Outline nol; + FT_Outline_New(render_priv->ftlibrary, ol->n_points, + ol->n_contours, &nol); + FT_Outline_Copy(ol, &nol); + + FT_Outline_Embolden(ol, sx * 2); + FT_Outline_Translate(ol, -sx, -sx); + FT_Outline_Embolden(&nol, sy * 2); + FT_Outline_Translate(&nol, -sy, -sy); + + for (i = 0; i < ol->n_points; i++) + ol->points[i].y = nol.points[i].y; + + FT_Outline_Done(render_priv->ftlibrary, &nol); + } } /** @@ -1428,60 +1899,96 @@ static void free_render_context(void) * and add them to cache. * The glyphs are returned in info->glyph and info->outline_glyph */ -static void get_outline_glyph(int symbol, glyph_info_t* info, FT_Vector* advance) +static void +get_outline_glyph(ass_renderer_t *render_priv, int symbol, + glyph_info_t *info, FT_Vector *advance, + ass_drawing_t *drawing) { - int error; - glyph_hash_val_t* val; - glyph_hash_key_t key; - memset(&key, 0, sizeof(key)); - key.font = render_context.font; - key.size = render_context.font_size; - key.ch = symbol; - key.scale_x = (render_context.scale_x * 0xFFFF); - key.scale_y = (render_context.scale_y * 0xFFFF); - key.advance = *advance; - key.bold = render_context.bold; - key.italic = render_context.italic; - key.outline = render_context.border * 0xFFFF; + glyph_hash_val_t *val; + glyph_hash_key_t key; + memset(&key, 0, sizeof(key)); - memset(info, 0, sizeof(glyph_info_t)); + if (drawing->hash) { + key.scale_x = double_to_d16(render_priv->state.scale_x); + key.scale_y = double_to_d16(render_priv->state.scale_y); + key.advance = *advance; + key.outline.x = render_priv->state.border_x * 0xFFFF; + key.outline.y = render_priv->state.border_y * 0xFFFF; + key.drawing_hash = drawing->hash; + } else { + key.font = render_priv->state.font; + key.size = render_priv->state.font_size; + key.ch = symbol; + key.bold = render_priv->state.bold; + key.italic = render_priv->state.italic; + key.scale_x = double_to_d16(render_priv->state.scale_x); + key.scale_y = double_to_d16(render_priv->state.scale_y); + key.advance = *advance; + key.outline.x = render_priv->state.border_x * 0xFFFF; + key.outline.y = render_priv->state.border_y * 0xFFFF; + key.flags = render_priv->state.flags; + } + memset(info, 0, sizeof(glyph_info_t)); - val = cache_find_glyph(&key); - if (val) { - FT_Glyph_Copy(val->glyph, &info->glyph); - if (val->outline_glyph) - FT_Glyph_Copy(val->outline_glyph, &info->outline_glyph); - info->bbox = val->bbox_scaled; - info->advance.x = val->advance.x; - info->advance.y = val->advance.y; - } else { - glyph_hash_val_t v; - info->glyph = ass_font_get_glyph(frame_context.ass_priv->fontconfig_priv, render_context.font, symbol, global_settings->hinting); - if (!info->glyph) - return; - info->advance.x = d16_to_d6(info->glyph->advance.x); - info->advance.y = d16_to_d6(info->glyph->advance.y); - FT_Glyph_Get_CBox( info->glyph, FT_GLYPH_BBOX_PIXELS, &info->bbox); + val = cache_find_glyph(render_priv->cache.glyph_cache, &key); + if (val) { + FT_Glyph_Copy(val->glyph, &info->glyph); + if (val->outline_glyph) + FT_Glyph_Copy(val->outline_glyph, &info->outline_glyph); + info->bbox = val->bbox_scaled; + info->advance.x = val->advance.x; + info->advance.y = val->advance.y; + if (drawing->hash) { + drawing->asc = val->asc; + drawing->desc = val->desc; + } + } else { + glyph_hash_val_t v; + if (drawing->hash) { + ass_drawing_parse(drawing); + FT_Glyph_Copy((FT_Glyph) drawing->glyph, &info->glyph); + } else { + info->glyph = + ass_font_get_glyph(render_priv->fontconfig_priv, + render_priv->state.font, symbol, + render_priv->settings.hinting, + render_priv->state.flags); + } + if (!info->glyph) + return; + info->advance.x = d16_to_d6(info->glyph->advance.x); + info->advance.y = d16_to_d6(info->glyph->advance.y); + FT_Glyph_Get_CBox(info->glyph, FT_GLYPH_BBOX_SUBPIXELS, &info->bbox); - if (render_context.stroker) { - info->outline_glyph = info->glyph; - error = FT_Glyph_StrokeBorder( &(info->outline_glyph), render_context.stroker, 0 , 0 ); // don't destroy original - if (error) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_FT_Glyph_Stroke_Error, error); - } - } + if (render_priv->state.border_x > 0 || + render_priv->state.border_y > 0) { - memset(&v, 0, sizeof(v)); - FT_Glyph_Copy(info->glyph, &v.glyph); - if (info->outline_glyph) - FT_Glyph_Copy(info->outline_glyph, &v.outline_glyph); - v.advance = info->advance; - v.bbox_scaled = info->bbox; - cache_add_glyph(&key, &v); - } + FT_Glyph_Copy(info->glyph, &info->outline_glyph); + stroke_outline_glyph(render_priv, + (FT_OutlineGlyph *) &info->outline_glyph, + double_to_d6(render_priv->state.border_x * + render_priv->border_scale), + double_to_d6(render_priv->state.border_y * + render_priv->border_scale)); + } + + memset(&v, 0, sizeof(v)); + FT_Glyph_Copy(info->glyph, &v.glyph); + if (info->outline_glyph) + FT_Glyph_Copy(info->outline_glyph, &v.outline_glyph); + v.advance = info->advance; + v.bbox_scaled = info->bbox; + if (drawing->hash) { + v.asc = drawing->asc; + v.desc = drawing->desc; + } + cache_add_glyph(render_priv->cache.glyph_cache, &key, &v); + } } -static void transform_3d(FT_Vector shift, FT_Glyph* glyph, FT_Glyph* glyph2, double frx, double fry, double frz); +static void transform_3d(FT_Vector shift, FT_Glyph *glyph, + FT_Glyph *glyph2, double frx, double fry, + double frz, double fax, double fay, double scale); /** * \brief Get bitmaps for a glyph @@ -1491,50 +1998,67 @@ static void transform_3d(FT_Vector shift, FT_Glyph* glyph, FT_Glyph* glyph2, dou * After that, bitmaps are added to the cache. * They are returned in info->bm (glyph), info->bm_o (outline) and info->bm_s (shadow). */ -static void get_bitmap_glyph(glyph_info_t* info) +static void +get_bitmap_glyph(ass_renderer_t *render_priv, glyph_info_t *info) { - bitmap_hash_val_t* val; - bitmap_hash_key_t* key = &info->hash_key; - - val = cache_find_bitmap(key); -/* val = 0; */ - - if (val) { - info->bm = val->bm; - info->bm_o = val->bm_o; - info->bm_s = val->bm_s; - } else { - FT_Vector shift; - bitmap_hash_val_t hash_val; - int error; - info->bm = info->bm_o = info->bm_s = 0; - if (info->glyph && info->symbol != '\n' && info->symbol != 0) { - // calculating rotation shift vector (from rotation origin to the glyph basepoint) - shift.x = int_to_d6(info->hash_key.shift_x); - shift.y = int_to_d6(info->hash_key.shift_y); - // apply rotation - transform_3d(shift, &info->glyph, &info->outline_glyph, info->frx, info->fry, info->frz); + bitmap_hash_val_t *val; + bitmap_hash_key_t *key = &info->hash_key; - // render glyph - error = glyph_to_bitmap(ass_renderer->synth_priv, - info->glyph, info->outline_glyph, - &info->bm, &info->bm_o, - &info->bm_s, info->be, info->blur * frame_context.border_scale); - if (error) - info->symbol = 0; + val = cache_find_bitmap(render_priv->cache.bitmap_cache, key); - // add bitmaps to cache - hash_val.bm_o = info->bm_o; - hash_val.bm = info->bm; - hash_val.bm_s = info->bm_s; - cache_add_bitmap(&(info->hash_key), &hash_val); - } - } - // deallocate glyphs - if (info->glyph) - FT_Done_Glyph(info->glyph); - if (info->outline_glyph) - FT_Done_Glyph(info->outline_glyph); + if (val) { + info->bm = val->bm; + info->bm_o = val->bm_o; + info->bm_s = val->bm_s; + } else { + FT_Vector shift; + bitmap_hash_val_t hash_val; + int error; + info->bm = info->bm_o = info->bm_s = 0; + if (info->glyph && info->symbol != '\n' && info->symbol != 0) { + // calculating rotation shift vector (from rotation origin to the glyph basepoint) + shift.x = info->hash_key.shift_x; + shift.y = info->hash_key.shift_y; + // apply rotation + transform_3d(shift, &info->glyph, &info->outline_glyph, + info->frx, info->fry, info->frz, info->fax, + info->fay, render_priv->font_scale); + + // subpixel shift + if (info->glyph) + FT_Outline_Translate( + &((FT_OutlineGlyph) info->glyph)->outline, + info->hash_key.advance.x, + -info->hash_key.advance.y); + if (info->outline_glyph) + FT_Outline_Translate( + &((FT_OutlineGlyph) info->outline_glyph)->outline, + info->hash_key.advance.x, + -info->hash_key.advance.y); + + // render glyph + error = glyph_to_bitmap(render_priv->synth_priv, + info->glyph, info->outline_glyph, + &info->bm, &info->bm_o, + &info->bm_s, info->be, + info->blur * render_priv->border_scale, + info->hash_key.shadow_offset); + if (error) + info->symbol = 0; + + // add bitmaps to cache + hash_val.bm_o = info->bm_o; + hash_val.bm = info->bm; + hash_val.bm_s = info->bm_s; + cache_add_bitmap(render_priv->cache.bitmap_cache, + &(info->hash_key), &hash_val); + } + } + // deallocate glyphs + if (info->glyph) + FT_Done_Glyph(info->glyph); + if (info->outline_glyph) + FT_Done_Glyph(info->outline_glyph); } /** @@ -1545,28 +2069,32 @@ static void get_bitmap_glyph(glyph_info_t* info) * lines[].asc * lines[].desc */ -static void measure_text(void) +static void measure_text(ass_renderer_t *render_priv) { - int cur_line = 0, max_asc = 0, max_desc = 0; - int i; - text_info.height = 0; - for (i = 0; i < text_info.length + 1; ++i) { - if ((i == text_info.length) || text_info.glyphs[i].linebreak) { - text_info.lines[cur_line].asc = max_asc; - text_info.lines[cur_line].desc = max_desc; - text_info.height += max_asc + max_desc; - cur_line ++; - max_asc = max_desc = 0; - } - if (i < text_info.length) { - glyph_info_t* cur = text_info.glyphs + i; - if (cur->asc > max_asc) - max_asc = cur->asc; - if (cur->desc > max_desc) - max_desc = cur->desc; - } - } - text_info.height += (text_info.n_lines - 1) * double_to_d6(global_settings->line_spacing); + text_info_t *text_info = &render_priv->text_info; + int cur_line = 0; + double max_asc = 0., max_desc = 0.; + int i; + text_info->height = 0.; + for (i = 0; i < text_info->length + 1; ++i) { + if ((i == text_info->length) || text_info->glyphs[i].linebreak) { + text_info->lines[cur_line].asc = max_asc; + text_info->lines[cur_line].desc = max_desc; + text_info->height += max_asc + max_desc; + cur_line++; + max_asc = max_desc = 0.; + } + if (i < text_info->length) { + glyph_info_t *cur = text_info->glyphs + i; + if (d6_to_double(cur->asc) > max_asc) + max_asc = d6_to_double(cur->asc); + if (d6_to_double(cur->desc) > max_desc) + max_desc = d6_to_double(cur->desc); + } + } + text_info->height += + (text_info->n_lines - + 1) * render_priv->settings.line_spacing; } /** @@ -1578,198 +2106,223 @@ static void measure_text(void) * the difference in lengths between this two lines. * The result may not be optimal, but usually is good enough. */ -static void wrap_lines_smart(int max_text_width) +static void +wrap_lines_smart(ass_renderer_t *render_priv, double max_text_width) { - int i, j; - glyph_info_t *cur, *s1, *e1, *s2, *s3, *w; - int last_space; - int break_type; - int exit; - int pen_shift_x; - int pen_shift_y; - int cur_line; + int i; + glyph_info_t *cur, *s1, *e1, *s2, *s3, *w; + int last_space; + int break_type; + int exit; + double pen_shift_x; + double pen_shift_y; + int cur_line; + text_info_t *text_info = &render_priv->text_info; - last_space = -1; - text_info.n_lines = 1; - break_type = 0; - s1 = text_info.glyphs; // current line start - for (i = 0; i < text_info.length; ++i) { - int break_at, s_offset, len; - cur = text_info.glyphs + i; - break_at = -1; - s_offset = s1->bbox.xMin + s1->pos.x; - len = (cur->bbox.xMax + cur->pos.x) - s_offset; + last_space = -1; + text_info->n_lines = 1; + break_type = 0; + s1 = text_info->glyphs; // current line start + for (i = 0; i < text_info->length; ++i) { + int break_at; + double s_offset, len; + cur = text_info->glyphs + i; + break_at = -1; + s_offset = d6_to_double(s1->bbox.xMin + s1->pos.x); + len = d6_to_double(cur->bbox.xMax + cur->pos.x) - s_offset; - if (cur->symbol == '\n') { - break_type = 2; - break_at = i; - mp_msg(MSGT_ASS, MSGL_DBG2, "forced line break at %d\n", break_at); - } - - if (len >= max_text_width) { - break_type = 1; - break_at = last_space; - if (break_at == -1) - break_at = i - 1; - if (break_at == -1) - break_at = 0; - mp_msg(MSGT_ASS, MSGL_DBG2, "overfill at %d\n", i); - mp_msg(MSGT_ASS, MSGL_DBG2, "line break at %d\n", break_at); - } + if (cur->symbol == '\n') { + break_type = 2; + break_at = i; + ass_msg(MSGL_DBG2, "forced line break at %d\n", + break_at); + } - if (break_at != -1) { - // need to use one more line - // marking break_at+1 as start of a new line - int lead = break_at + 1; // the first symbol of the new line - if (text_info.n_lines >= MAX_LINES) { - // to many lines ! - // no more linebreaks - for (j = lead; j < text_info.length; ++j) - text_info.glyphs[j].linebreak = 0; - break; - } - if (lead < text_info.length) - text_info.glyphs[lead].linebreak = break_type; - last_space = -1; - s1 = text_info.glyphs + lead; - s_offset = s1->bbox.xMin + s1->pos.x; - text_info.n_lines ++; - } - - if (cur->symbol == ' ') - last_space = i; + if ((len >= max_text_width) + && (render_priv->track->WrapStyle != 2)) { + break_type = 1; + break_at = last_space; + if (break_at == -1) + break_at = i - 1; + if (break_at == -1) + break_at = 0; + ass_msg(MSGL_DBG2, "overfill at %d\n", i); + ass_msg(MSGL_DBG2, "line break at %d\n", break_at); + } - // make sure the hard linebreak is not forgotten when - // there was a new soft linebreak just inserted - if (cur->symbol == '\n' && break_type == 1) - i--; - } + if (break_at != -1) { + // need to use one more line + // marking break_at+1 as start of a new line + int lead = break_at + 1; // the first symbol of the new line + if (text_info->n_lines >= text_info->max_lines) { + // Raise maximum number of lines + text_info->max_lines *= 2; + text_info->lines = realloc(text_info->lines, + sizeof(line_info_t) * + text_info->max_lines); + } + if (lead < text_info->length) + text_info->glyphs[lead].linebreak = break_type; + last_space = -1; + s1 = text_info->glyphs + lead; + s_offset = d6_to_double(s1->bbox.xMin + s1->pos.x); + text_info->n_lines++; + } + + if (cur->symbol == ' ') + last_space = i; + + // make sure the hard linebreak is not forgotten when + // there was a new soft linebreak just inserted + if (cur->symbol == '\n' && break_type == 1) + i--; + } #define DIFF(x,y) (((x) < (y)) ? (y - x) : (x - y)) - exit = 0; - while (!exit) { - exit = 1; - w = s3 = text_info.glyphs; - s1 = s2 = 0; - for (i = 0; i <= text_info.length; ++i) { - cur = text_info.glyphs + i; - if ((i == text_info.length) || cur->linebreak) { - s1 = s2; - s2 = s3; - s3 = cur; - if (s1 && (s2->linebreak == 1)) { // have at least 2 lines, and linebreak is 'soft' - int l1, l2, l1_new, l2_new; + exit = 0; + while (!exit) { + exit = 1; + w = s3 = text_info->glyphs; + s1 = s2 = 0; + for (i = 0; i <= text_info->length; ++i) { + cur = text_info->glyphs + i; + if ((i == text_info->length) || cur->linebreak) { + s1 = s2; + s2 = s3; + s3 = cur; + if (s1 && (s2->linebreak == 1)) { // have at least 2 lines, and linebreak is 'soft' + double l1, l2, l1_new, l2_new; - w = s2; - do { --w; } while ((w > s1) && (w->symbol == ' ')); - while ((w > s1) && (w->symbol != ' ')) { --w; } - e1 = w; - while ((e1 > s1) && (e1->symbol == ' ')) { --e1; } - if (w->symbol == ' ') ++w; + w = s2; + do { + --w; + } while ((w > s1) && (w->symbol == ' ')); + while ((w > s1) && (w->symbol != ' ')) { + --w; + } + e1 = w; + while ((e1 > s1) && (e1->symbol == ' ')) { + --e1; + } + if (w->symbol == ' ') + ++w; - l1 = ((s2-1)->bbox.xMax + (s2-1)->pos.x) - (s1->bbox.xMin + s1->pos.x); - l2 = ((s3-1)->bbox.xMax + (s3-1)->pos.x) - (s2->bbox.xMin + s2->pos.x); - l1_new = (e1->bbox.xMax + e1->pos.x) - (s1->bbox.xMin + s1->pos.x); - l2_new = ((s3-1)->bbox.xMax + (s3-1)->pos.x) - (w->bbox.xMin + w->pos.x); + l1 = d6_to_double(((s2 - 1)->bbox.xMax + (s2 - 1)->pos.x) - + (s1->bbox.xMin + s1->pos.x)); + l2 = d6_to_double(((s3 - 1)->bbox.xMax + (s3 - 1)->pos.x) - + (s2->bbox.xMin + s2->pos.x)); + l1_new = d6_to_double( + (e1->bbox.xMax + e1->pos.x) - + (s1->bbox.xMin + s1->pos.x)); + l2_new = d6_to_double( + ((s3 - 1)->bbox.xMax + (s3 - 1)->pos.x) - + (w->bbox.xMin + w->pos.x)); - if (DIFF(l1_new, l2_new) < DIFF(l1, l2)) { - w->linebreak = 1; - s2->linebreak = 0; - exit = 0; - } - } - } - if (i == text_info.length) - break; - } - - } - assert(text_info.n_lines >= 1); + if (DIFF(l1_new, l2_new) < DIFF(l1, l2)) { + w->linebreak = 1; + s2->linebreak = 0; + exit = 0; + } + } + } + if (i == text_info->length) + break; + } + + } + assert(text_info->n_lines >= 1); #undef DIFF - - measure_text(); - pen_shift_x = 0; - pen_shift_y = 0; - cur_line = 1; - for (i = 0; i < text_info.length; ++i) { - cur = text_info.glyphs + i; - if (cur->linebreak) { - int height = text_info.lines[cur_line - 1].desc + text_info.lines[cur_line].asc; - cur_line ++; - pen_shift_x = - cur->pos.x; - pen_shift_y += d6_to_int(height + double_to_d6(global_settings->line_spacing)); - mp_msg(MSGT_ASS, MSGL_DBG2, "shifting from %d to %d by (%d, %d)\n", i, text_info.length - 1, pen_shift_x, pen_shift_y); - } - cur->pos.x += pen_shift_x; - cur->pos.y += pen_shift_y; - } + measure_text(render_priv); + + pen_shift_x = 0.; + pen_shift_y = 0.; + cur_line = 1; + for (i = 0; i < text_info->length; ++i) { + cur = text_info->glyphs + i; + if (cur->linebreak) { + double height = + text_info->lines[cur_line - 1].desc + + text_info->lines[cur_line].asc; + cur_line++; + pen_shift_x = d6_to_double(-cur->pos.x); + pen_shift_y += height + render_priv->settings.line_spacing; + ass_msg(MSGL_DBG2, + "shifting from %d to %d by (%f, %f)\n", i, + text_info->length - 1, pen_shift_x, pen_shift_y); + } + cur->pos.x += double_to_d6(pen_shift_x); + cur->pos.y += double_to_d6(pen_shift_y); + } } /** * \brief determine karaoke effects * Karaoke effects cannot be calculated during parse stage (get_next_char()), * so they are done in a separate step. - * Parse stage: when karaoke style override is found, its parameters are stored in the next glyph's + * Parse stage: when karaoke style override is found, its parameters are stored in the next glyph's * (the first glyph of the karaoke word)'s effect_type and effect_timing. * This function: * 1. sets effect_type for all glyphs in the word (_karaoke_ word) * 2. sets effect_timing for all glyphs to x coordinate of the border line between the left and right karaoke parts * (left part is filled with PrimaryColour, right one - with SecondaryColour). */ -static void process_karaoke_effects(void) +static void process_karaoke_effects(ass_renderer_t *render_priv) { - glyph_info_t *cur, *cur2; - glyph_info_t *s1, *e1; // start and end of the current word - glyph_info_t *s2; // start of the next word - int i; - int timing; // current timing - int tm_start, tm_end; // timings at start and end of the current word - int tm_current; - double dt; - int x; - int x_start, x_end; + glyph_info_t *cur, *cur2; + glyph_info_t *s1, *e1; // start and end of the current word + glyph_info_t *s2; // start of the next word + int i; + int timing; // current timing + int tm_start, tm_end; // timings at start and end of the current word + int tm_current; + double dt; + int x; + int x_start, x_end; - tm_current = frame_context.time - render_context.event->Start; - timing = 0; - s1 = s2 = 0; - for (i = 0; i <= text_info.length; ++i) { - cur = text_info.glyphs + i; - if ((i == text_info.length) || (cur->effect_type != EF_NONE)) { - s1 = s2; - s2 = cur; - if (s1) { - e1 = s2 - 1; - tm_start = timing + s1->effect_skip_timing; - tm_end = tm_start + s1->effect_timing; - timing = tm_end; - x_start = 1000000; - x_end = -1000000; - for (cur2 = s1; cur2 <= e1; ++cur2) { - x_start = FFMIN(x_start, cur2->bbox.xMin + cur2->pos.x); - x_end = FFMAX(x_end, cur2->bbox.xMax + cur2->pos.x); - } + tm_current = render_priv->time - render_priv->state.event->Start; + timing = 0; + s1 = s2 = 0; + for (i = 0; i <= render_priv->text_info.length; ++i) { + cur = render_priv->text_info.glyphs + i; + if ((i == render_priv->text_info.length) + || (cur->effect_type != EF_NONE)) { + s1 = s2; + s2 = cur; + if (s1) { + e1 = s2 - 1; + tm_start = timing + s1->effect_skip_timing; + tm_end = tm_start + s1->effect_timing; + timing = tm_end; + x_start = 1000000; + x_end = -1000000; + for (cur2 = s1; cur2 <= e1; ++cur2) { + x_start = FFMIN(x_start, d6_to_int(cur2->bbox.xMin + cur2->pos.x)); + x_end = FFMAX(x_end, d6_to_int(cur2->bbox.xMax + cur2->pos.x)); + } - dt = (tm_current - tm_start); - if ((s1->effect_type == EF_KARAOKE) || (s1->effect_type == EF_KARAOKE_KO)) { - if (dt > 0) - x = x_end + 1; - else - x = x_start; - } else if (s1->effect_type == EF_KARAOKE_KF) { - dt /= (tm_end - tm_start); - x = x_start + (x_end - x_start) * dt; - } else { - mp_msg(MSGT_ASS, MSGL_ERR, MSGTR_LIBASS_UnknownEffectType_InternalError); - continue; - } + dt = (tm_current - tm_start); + if ((s1->effect_type == EF_KARAOKE) + || (s1->effect_type == EF_KARAOKE_KO)) { + if (dt > 0) + x = x_end + 1; + else + x = x_start; + } else if (s1->effect_type == EF_KARAOKE_KF) { + dt /= (tm_end - tm_start); + x = x_start + (x_end - x_start) * dt; + } else { + ass_msg(MSGL_ERR, + MSGTR_LIBASS_UnknownEffectType_InternalError); + continue; + } - for (cur2 = s1; cur2 <= e1; ++cur2) { - cur2->effect_type = s1->effect_type; - cur2->effect_timing = x - cur2->pos.x; - } - } - } - } + for (cur2 = s1; cur2 <= e1; ++cur2) { + cur2->effect_type = s1->effect_type; + cur2->effect_timing = x - d6_to_int(cur2->pos.x); + } + } + } + } } /** @@ -1778,34 +2331,34 @@ static void process_karaoke_effects(void) * \param alignment alignment * \param bx, by out: base point coordinates */ -static void get_base_point(FT_BBox bbox, int alignment, int* bx, int* by) +static void get_base_point(double_bbox_t *bbox, int alignment, double *bx, double *by) { - const int halign = alignment & 3; - const int valign = alignment & 12; - if (bx) - switch(halign) { - case HALIGN_LEFT: - *bx = bbox.xMin; - break; - case HALIGN_CENTER: - *bx = (bbox.xMax + bbox.xMin) / 2; - break; - case HALIGN_RIGHT: - *bx = bbox.xMax; - break; - } - if (by) - switch(valign) { - case VALIGN_TOP: - *by = bbox.yMin; - break; - case VALIGN_CENTER: - *by = (bbox.yMax + bbox.yMin) / 2; - break; - case VALIGN_SUB: - *by = bbox.yMax; - break; - } + const int halign = alignment & 3; + const int valign = alignment & 12; + if (bx) + switch (halign) { + case HALIGN_LEFT: + *bx = bbox->xMin; + break; + case HALIGN_CENTER: + *bx = (bbox->xMax + bbox->xMin) / 2.0; + break; + case HALIGN_RIGHT: + *bx = bbox->xMax; + break; + } + if (by) + switch (valign) { + case VALIGN_TOP: + *by = bbox->yMin; + break; + case VALIGN_CENTER: + *by = (bbox->yMax + bbox->yMin) / 2.0; + break; + case VALIGN_SUB: + *by = bbox->yMax; + break; + } } /** @@ -1813,42 +2366,47 @@ static void get_base_point(FT_BBox bbox, int alignment, int* bx, int* by) * Applies rotations given by frx, fry and frz and projects the points back * onto the screen plane. */ -static void transform_3d_points(FT_Vector shift, FT_Glyph glyph, double frx, double fry, double frz) { - double sx = sin(frx); - double sy = sin(fry); - double sz = sin(frz); - double cx = cos(frx); - double cy = cos(fry); - double cz = cos(frz); - FT_Outline *outline = &((FT_OutlineGlyph) glyph)->outline; - FT_Vector* p = outline->points; - double x, y, z, xx, yy, zz; - int i; +static void +transform_3d_points(FT_Vector shift, FT_Glyph glyph, double frx, + double fry, double frz, double fax, double fay, + double scale) +{ + double sx = sin(frx); + double sy = sin(fry); + double sz = sin(frz); + double cx = cos(frx); + double cy = cos(fry); + double cz = cos(frz); + FT_Outline *outline = &((FT_OutlineGlyph) glyph)->outline; + FT_Vector *p = outline->points; + double x, y, z, xx, yy, zz; + int i, dist; - for (i=0; in_points; i++) { - x = p[i].x + shift.x; - y = p[i].y + shift.y; - z = 0.; + dist = 20000 * scale; + for (i = 0; i < outline->n_points; i++) { + x = (double) p[i].x + shift.x + (-fax * p[i].y); + y = (double) p[i].y + shift.y + (-fay * p[i].x); + z = 0.; - xx = x*cz + y*sz; - yy = -(x*sz - y*cz); - zz = z; + xx = x * cz + y * sz; + yy = -(x * sz - y * cz); + zz = z; - x = xx; - y = yy*cx + zz*sx; - z = yy*sx - zz*cx; + x = xx; + y = yy * cx + zz * sx; + z = yy * sx - zz * cx; - xx = x*cy + z*sy; - yy = y; - zz = x*sy - z*cy; + xx = x * cy + z * sy; + yy = y; + zz = x * sy - z * cy; - zz = FFMAX(zz, -19000); + zz = FFMAX(zz, 1000 - dist); - x = (xx * 20000) / (zz + 20000); - y = (yy * 20000) / (zz + 20000); - p[i].x = x - shift.x + 0.5; - p[i].y = y - shift.y + 0.5; - } + x = (xx * dist) / (zz + dist); + y = (yy * dist) / (zz + dist); + p[i].x = x - shift.x + 0.5; + p[i].y = y - shift.y + 0.5; + } } /** @@ -1861,17 +2419,22 @@ static void transform_3d_points(FT_Vector shift, FT_Glyph glyph, double frx, dou * \param frz z-axis rotation angle * Rotates both glyphs by frx, fry and frz. Shift vector is added before rotation and subtracted after it. */ -static void transform_3d(FT_Vector shift, FT_Glyph* glyph, FT_Glyph* glyph2, double frx, double fry, double frz) +static void +transform_3d(FT_Vector shift, FT_Glyph *glyph, FT_Glyph *glyph2, + double frx, double fry, double frz, double fax, double fay, + double scale) { - frx = - frx; - frz = - frz; - if (frx != 0. || fry != 0. || frz != 0.) { - if (glyph && *glyph) - transform_3d_points(shift, *glyph, frx, fry, frz); + frx = -frx; + frz = -frz; + if (frx != 0. || fry != 0. || frz != 0. || fax != 0. || fay != 0.) { + if (glyph && *glyph) + transform_3d_points(shift, *glyph, frx, fry, frz, + fax, fay, scale); - if (glyph2 && *glyph2) - transform_3d_points(shift, *glyph2, frx, fry, frz); - } + if (glyph2 && *glyph2) + transform_3d_points(shift, *glyph2, frx, fry, frz, + fax, fay, scale); + } } @@ -1881,615 +2444,779 @@ static void transform_3d(FT_Vector shift, FT_Glyph* glyph, FT_Glyph* glyph2, dou * \param event_images struct containing resulting images, will also be initialized * Process event, appending resulting ass_image_t's to images_root. */ -static int ass_render_event(ass_event_t* event, event_images_t* event_images) +static int +ass_render_event(ass_renderer_t *render_priv, ass_event_t *event, + event_images_t *event_images) { - char* p; - FT_UInt previous; - FT_UInt num_glyphs; - FT_Vector pen; - unsigned code; - FT_BBox bbox; - int i, j; - FT_Vector shift; - int MarginL, MarginR, MarginV; - int last_break; - int alignment, halign, valign; - int device_x = 0, device_y = 0; + char *p; + FT_UInt previous; + FT_UInt num_glyphs; + FT_Vector pen; + unsigned code; + double_bbox_t bbox; + int i, j; + FT_Vector shift = { .x = 0, .y = 0}; + int MarginL, MarginR, MarginV; + int last_break; + int alignment, halign, valign; + double device_x = 0; + double device_y = 0; + text_info_t *text_info = &render_priv->text_info; + ass_drawing_t *drawing; - if (event->Style >= frame_context.track->n_styles) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_NoStyleFound); - return 1; - } - if (!event->Text) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_EmptyEvent); - return 1; - } + if (event->Style >= render_priv->track->n_styles) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_NoStyleFound); + return 1; + } + if (!event->Text) { + ass_msg(MSGL_WARN, MSGTR_LIBASS_EmptyEvent); + return 1; + } - init_render_context(event); + init_render_context(render_priv, event); - text_info.length = 0; - pen.x = 0; - pen.y = 0; - previous = 0; - num_glyphs = 0; - p = event->Text; - // Event parsing. - while (1) { - // get next char, executing style override - // this affects render_context - do { - code = get_next_char(&p); - } while (code && render_context.drawing_mode); // skip everything in drawing mode - - // face could have been changed in get_next_char - if (!render_context.font) { - free_render_context(); - return 1; - } + drawing = render_priv->state.drawing; + text_info->length = 0; + pen.x = 0; + pen.y = 0; + previous = 0; + num_glyphs = 0; + p = event->Text; + // Event parsing. + while (1) { + // get next char, executing style override + // this affects render_context + do { + code = get_next_char(render_priv, &p); + if (render_priv->state.drawing_mode && code) + ass_drawing_add_char(drawing, (char) code); + } while (code && render_priv->state.drawing_mode); // skip everything in drawing mode - if (code == 0) - break; + // Parse drawing + if (drawing->i) { + drawing->scale_x = render_priv->state.scale_x * + render_priv->font_scale_x * + render_priv->font_scale; + drawing->scale_y = render_priv->state.scale_y * + render_priv->font_scale; + ass_drawing_hash(drawing); + p--; + code = -1; + } - if (text_info.length >= MAX_GLYPHS) { - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_MAX_GLYPHS_Reached, - (int)(event - frame_context.track->events), event->Start, event->Duration, event->Text); - break; - } + // face could have been changed in get_next_char + if (!render_priv->state.font) { + free_render_context(render_priv); + return 1; + } - if ( previous && code ) { - FT_Vector delta; - delta = ass_font_get_kerning(render_context.font, previous, code); - pen.x += delta.x * render_context.scale_x; - pen.y += delta.y * render_context.scale_y; - } + if (code == 0) + break; - shift.x = pen.x & SUBPIXEL_MASK; - shift.y = pen.y & SUBPIXEL_MASK; + // Insert space between two forced breaks to create empty lines + // FIXME: should probably be done in wrap_lines_smart, + // this is a hack + if (previous == '\n' && code == '\n') { + code = ' '; + p -= 2; + } - if (render_context.evt_type == EVENT_POSITIONED) { - shift.x += double_to_d6(x2scr_pos(render_context.pos_x)) & SUBPIXEL_MASK; - shift.y -= double_to_d6(y2scr_pos(render_context.pos_y)) & SUBPIXEL_MASK; - } + if (text_info->length >= text_info->max_glyphs) { + // Raise maximum number of glyphs + text_info->max_glyphs *= 2; + text_info->glyphs = + realloc(text_info->glyphs, + sizeof(glyph_info_t) * text_info->max_glyphs); + } - ass_font_set_transform(render_context.font, - render_context.scale_x * frame_context.font_scale_x, - render_context.scale_y, - &shift ); + // Add kerning to pen + if (previous && code && !drawing->hash) { + FT_Vector delta; + delta = + ass_font_get_kerning(render_priv->state.font, previous, + code); + pen.x += delta.x * render_priv->state.scale_x; + pen.y += delta.y * render_priv->state.scale_y; + } - get_outline_glyph(code, text_info.glyphs + text_info.length, &shift); - - text_info.glyphs[text_info.length].pos.x = pen.x >> 6; - text_info.glyphs[text_info.length].pos.y = pen.y >> 6; - - pen.x += text_info.glyphs[text_info.length].advance.x; - pen.x += double_to_d6(render_context.hspacing); - pen.y += text_info.glyphs[text_info.length].advance.y; - - previous = code; + ass_font_set_transform(render_priv->state.font, + render_priv->state.scale_x * + render_priv->font_scale_x, + render_priv->state.scale_y, &shift); - text_info.glyphs[text_info.length].symbol = code; - text_info.glyphs[text_info.length].linebreak = 0; - for (i = 0; i < 4; ++i) { - uint32_t clr = render_context.c[i]; - change_alpha(&clr, mult_alpha(_a(clr), render_context.fade), 1.); - text_info.glyphs[text_info.length].c[i] = clr; - } - text_info.glyphs[text_info.length].effect_type = render_context.effect_type; - text_info.glyphs[text_info.length].effect_timing = render_context.effect_timing; - text_info.glyphs[text_info.length].effect_skip_timing = render_context.effect_skip_timing; - text_info.glyphs[text_info.length].be = render_context.be; - text_info.glyphs[text_info.length].blur = render_context.blur; - text_info.glyphs[text_info.length].shadow = render_context.shadow; - text_info.glyphs[text_info.length].frx = render_context.frx; - text_info.glyphs[text_info.length].fry = render_context.fry; - text_info.glyphs[text_info.length].frz = render_context.frz; - ass_font_get_asc_desc(render_context.font, code, - &text_info.glyphs[text_info.length].asc, - &text_info.glyphs[text_info.length].desc); - text_info.glyphs[text_info.length].asc *= render_context.scale_y; - text_info.glyphs[text_info.length].desc *= render_context.scale_y; + get_outline_glyph(render_priv, code, + text_info->glyphs + text_info->length, &shift, + drawing); - // fill bitmap_hash_key - text_info.glyphs[text_info.length].hash_key.font = render_context.font; - text_info.glyphs[text_info.length].hash_key.size = render_context.font_size; - text_info.glyphs[text_info.length].hash_key.outline = render_context.border * 0xFFFF; - text_info.glyphs[text_info.length].hash_key.scale_x = render_context.scale_x * 0xFFFF; - text_info.glyphs[text_info.length].hash_key.scale_y = render_context.scale_y * 0xFFFF; - text_info.glyphs[text_info.length].hash_key.frx = render_context.frx * 0xFFFF; - text_info.glyphs[text_info.length].hash_key.fry = render_context.fry * 0xFFFF; - text_info.glyphs[text_info.length].hash_key.frz = render_context.frz * 0xFFFF; - text_info.glyphs[text_info.length].hash_key.bold = render_context.bold; - text_info.glyphs[text_info.length].hash_key.italic = render_context.italic; - text_info.glyphs[text_info.length].hash_key.ch = code; - text_info.glyphs[text_info.length].hash_key.advance = shift; - text_info.glyphs[text_info.length].hash_key.be = render_context.be; - text_info.glyphs[text_info.length].hash_key.blur = render_context.blur; + text_info->glyphs[text_info->length].pos.x = pen.x; + text_info->glyphs[text_info->length].pos.y = pen.y; - text_info.length++; + pen.x += text_info->glyphs[text_info->length].advance.x; + pen.x += double_to_d6(render_priv->state.hspacing * + render_priv->font_scale); + pen.y += text_info->glyphs[text_info->length].advance.y; + pen.y += render_priv->state.fay * + text_info->glyphs[text_info->length].advance.x; - render_context.effect_type = EF_NONE; - render_context.effect_timing = 0; - render_context.effect_skip_timing = 0; - } - - if (text_info.length == 0) { - // no valid symbols in the event; this can be smth like {comment} - free_render_context(); - return 1; - } - - // depends on glyph x coordinates being monotonous, so it should be done before line wrap - process_karaoke_effects(); - - // alignments - alignment = render_context.alignment; - halign = alignment & 3; - valign = alignment & 12; + previous = code; - MarginL = (event->MarginL) ? event->MarginL : render_context.style->MarginL; - MarginR = (event->MarginR) ? event->MarginR : render_context.style->MarginR; - MarginV = (event->MarginV) ? event->MarginV : render_context.style->MarginV; + text_info->glyphs[text_info->length].symbol = code; + text_info->glyphs[text_info->length].linebreak = 0; + for (i = 0; i < 4; ++i) { + uint32_t clr = render_priv->state.c[i]; + change_alpha(&clr, + mult_alpha(_a(clr), render_priv->state.fade), 1.); + text_info->glyphs[text_info->length].c[i] = clr; + } + text_info->glyphs[text_info->length].effect_type = + render_priv->state.effect_type; + text_info->glyphs[text_info->length].effect_timing = + render_priv->state.effect_timing; + text_info->glyphs[text_info->length].effect_skip_timing = + render_priv->state.effect_skip_timing; + text_info->glyphs[text_info->length].be = render_priv->state.be; + text_info->glyphs[text_info->length].blur = render_priv->state.blur; + text_info->glyphs[text_info->length].shadow_x = + render_priv->state.shadow_x; + text_info->glyphs[text_info->length].shadow_y = + render_priv->state.shadow_y; + text_info->glyphs[text_info->length].frx = render_priv->state.frx; + text_info->glyphs[text_info->length].fry = render_priv->state.fry; + text_info->glyphs[text_info->length].frz = render_priv->state.frz; + text_info->glyphs[text_info->length].fax = render_priv->state.fax; + text_info->glyphs[text_info->length].fay = render_priv->state.fay; + if (drawing->hash) { + text_info->glyphs[text_info->length].asc = drawing->asc; + text_info->glyphs[text_info->length].desc = drawing->desc; + } else { + ass_font_get_asc_desc(render_priv->state.font, code, + &text_info->glyphs[text_info->length].asc, + &text_info->glyphs[text_info->length].desc); - if (render_context.evt_type != EVENT_HSCROLL) { - int max_text_width; + text_info->glyphs[text_info->length].asc *= + render_priv->state.scale_y; + text_info->glyphs[text_info->length].desc *= + render_priv->state.scale_y; + } - // calculate max length of a line - max_text_width = x2scr(frame_context.track->PlayResX - MarginR) - x2scr(MarginL); + // fill bitmap_hash_key + if (!drawing->hash) { + text_info->glyphs[text_info->length].hash_key.font = + render_priv->state.font; + text_info->glyphs[text_info->length].hash_key.size = + render_priv->state.font_size; + text_info->glyphs[text_info->length].hash_key.bold = + render_priv->state.bold; + text_info->glyphs[text_info->length].hash_key.italic = + render_priv->state.italic; + } else + text_info->glyphs[text_info->length].hash_key.drawing_hash = + drawing->hash; + text_info->glyphs[text_info->length].hash_key.ch = code; + text_info->glyphs[text_info->length].hash_key.outline.x = + render_priv->state.border_x * 0xFFFF; + text_info->glyphs[text_info->length].hash_key.outline.y = + render_priv->state.border_y * 0xFFFF; + text_info->glyphs[text_info->length].hash_key.scale_x = + render_priv->state.scale_x * 0xFFFF; + text_info->glyphs[text_info->length].hash_key.scale_y = + render_priv->state.scale_y * 0xFFFF; + text_info->glyphs[text_info->length].hash_key.frx = + render_priv->state.frx * 0xFFFF; + text_info->glyphs[text_info->length].hash_key.fry = + render_priv->state.fry * 0xFFFF; + text_info->glyphs[text_info->length].hash_key.frz = + render_priv->state.frz * 0xFFFF; + text_info->glyphs[text_info->length].hash_key.fax = + render_priv->state.fax * 0xFFFF; + text_info->glyphs[text_info->length].hash_key.fay = + render_priv->state.fay * 0xFFFF; + text_info->glyphs[text_info->length].hash_key.advance.x = pen.x; + text_info->glyphs[text_info->length].hash_key.advance.y = pen.y; + text_info->glyphs[text_info->length].hash_key.be = + render_priv->state.be; + text_info->glyphs[text_info->length].hash_key.blur = + render_priv->state.blur; + text_info->glyphs[text_info->length].hash_key.shadow_offset.x = + double_to_d6( + render_priv->state.shadow_x * render_priv->border_scale - + (int) (render_priv->state.shadow_x * + render_priv->border_scale)); + text_info->glyphs[text_info->length].hash_key.shadow_offset.y = + double_to_d6( + render_priv->state.shadow_y * render_priv->border_scale - + (int) (render_priv->state.shadow_y * + render_priv->border_scale)); - // rearrange text in several lines - wrap_lines_smart(max_text_width); + text_info->length++; - // align text - last_break = -1; - for (i = 1; i < text_info.length + 1; ++i) { // (text_info.length + 1) is the end of the last line - if ((i == text_info.length) || text_info.glyphs[i].linebreak) { - int width, shift = 0; - glyph_info_t* first_glyph = text_info.glyphs + last_break + 1; - glyph_info_t* last_glyph = text_info.glyphs + i - 1; + render_priv->state.effect_type = EF_NONE; + render_priv->state.effect_timing = 0; + render_priv->state.effect_skip_timing = 0; - while ((last_glyph > first_glyph) && ((last_glyph->symbol == '\n') || (last_glyph->symbol == 0))) - last_glyph --; + if (drawing->hash) { + ass_drawing_free(drawing); + drawing = render_priv->state.drawing = + ass_drawing_new(render_priv->fontconfig_priv, + render_priv->state.font, + render_priv->settings.hinting, + render_priv->ftlibrary); + } + } - width = last_glyph->pos.x + d6_to_int(last_glyph->advance.x) - first_glyph->pos.x; - if (halign == HALIGN_LEFT) { // left aligned, no action - shift = 0; - } else if (halign == HALIGN_RIGHT) { // right aligned - shift = max_text_width - width; - } else if (halign == HALIGN_CENTER) { // centered - shift = (max_text_width - width) / 2; - } - for (j = last_break + 1; j < i; ++j) { - text_info.glyphs[j].pos.x += shift; - } - last_break = i - 1; - } - } - } else { // render_context.evt_type == EVENT_HSCROLL - measure_text(); - } - - // determing text bounding box - compute_string_bbox(&text_info, &bbox); - - // determine device coordinates for text - - // x coordinate for everything except positioned events - if (render_context.evt_type == EVENT_NORMAL || - render_context.evt_type == EVENT_VSCROLL) { - device_x = x2scr(MarginL); - } else if (render_context.evt_type == EVENT_HSCROLL) { - if (render_context.scroll_direction == SCROLL_RL) - device_x = x2scr(frame_context.track->PlayResX - render_context.scroll_shift); - else if (render_context.scroll_direction == SCROLL_LR) - device_x = x2scr(render_context.scroll_shift) - (bbox.xMax - bbox.xMin); - } + if (text_info->length == 0) { + // no valid symbols in the event; this can be smth like {comment} + free_render_context(render_priv); + return 1; + } + // depends on glyph x coordinates being monotonous, so it should be done before line wrap + process_karaoke_effects(render_priv); - // y coordinate for everything except positioned events - if (render_context.evt_type == EVENT_NORMAL || - render_context.evt_type == EVENT_HSCROLL) { - if (valign == VALIGN_TOP) { // toptitle - device_y = y2scr_top(MarginV) + d6_to_int(text_info.lines[0].asc); - } else if (valign == VALIGN_CENTER) { // midtitle - int scr_y = y2scr(frame_context.track->PlayResY / 2); - device_y = scr_y - (bbox.yMax - bbox.yMin) / 2; - } else { // subtitle - int scr_y; - if (valign != VALIGN_SUB) - mp_msg(MSGT_ASS, MSGL_V, "Invalid valign, supposing 0 (subtitle)\n"); - scr_y = y2scr_sub(frame_context.track->PlayResY - MarginV); - device_y = scr_y; - device_y -= d6_to_int(text_info.height); - device_y += d6_to_int(text_info.lines[0].asc); - } - } else if (render_context.evt_type == EVENT_VSCROLL) { - if (render_context.scroll_direction == SCROLL_TB) - device_y = y2scr(render_context.clip_y0 + render_context.scroll_shift) - (bbox.yMax - bbox.yMin); - else if (render_context.scroll_direction == SCROLL_BT) - device_y = y2scr(render_context.clip_y1 - render_context.scroll_shift); - } + // alignments + alignment = render_priv->state.alignment; + halign = alignment & 3; + valign = alignment & 12; - // positioned events are totally different - if (render_context.evt_type == EVENT_POSITIONED) { - int base_x = 0; - int base_y = 0; - mp_msg(MSGT_ASS, MSGL_DBG2, "positioned event at %f, %f\n", render_context.pos_x, render_context.pos_y); - get_base_point(bbox, alignment, &base_x, &base_y); - device_x = x2scr_pos(render_context.pos_x) - base_x; - device_y = y2scr_pos(render_context.pos_y) - base_y; - } - - // fix clip coordinates (they depend on alignment) - if (render_context.evt_type == EVENT_NORMAL || - render_context.evt_type == EVENT_HSCROLL || - render_context.evt_type == EVENT_VSCROLL) { - render_context.clip_x0 = x2scr(render_context.clip_x0); - render_context.clip_x1 = x2scr(render_context.clip_x1); - if (valign == VALIGN_TOP) { - render_context.clip_y0 = y2scr_top(render_context.clip_y0); - render_context.clip_y1 = y2scr_top(render_context.clip_y1); - } else if (valign == VALIGN_CENTER) { - render_context.clip_y0 = y2scr(render_context.clip_y0); - render_context.clip_y1 = y2scr(render_context.clip_y1); - } else if (valign == VALIGN_SUB) { - render_context.clip_y0 = y2scr_sub(render_context.clip_y0); - render_context.clip_y1 = y2scr_sub(render_context.clip_y1); - } - } else if (render_context.evt_type == EVENT_POSITIONED) { - render_context.clip_x0 = x2scr_pos(render_context.clip_x0); - render_context.clip_x1 = x2scr_pos(render_context.clip_x1); - render_context.clip_y0 = y2scr_pos(render_context.clip_y0); - render_context.clip_y1 = y2scr_pos(render_context.clip_y1); - } + MarginL = + (event->MarginL) ? event->MarginL : render_priv->state.style-> + MarginL; + MarginR = + (event->MarginR) ? event->MarginR : render_priv->state.style-> + MarginR; + MarginV = + (event->MarginV) ? event->MarginV : render_priv->state.style-> + MarginV; - // calculate rotation parameters - { - FT_Vector center; - - if (render_context.have_origin) { - center.x = x2scr(render_context.org_x); - center.y = y2scr(render_context.org_y); - } else { - int bx = 0, by = 0; - get_base_point(bbox, alignment, &bx, &by); - center.x = device_x + bx; - center.y = device_y + by; - } + if (render_priv->state.evt_type != EVENT_HSCROLL) { + double max_text_width; - for (i = 0; i < text_info.length; ++i) { - glyph_info_t* info = text_info.glyphs + i; + // calculate max length of a line + max_text_width = + x2scr(render_priv, + render_priv->track->PlayResX - MarginR) - + x2scr(render_priv, MarginL); - if (info->hash_key.frx || info->hash_key.fry || info->hash_key.frz) { - info->hash_key.shift_x = info->pos.x + device_x - center.x; - info->hash_key.shift_y = - (info->pos.y + device_y - center.y); - } else { - info->hash_key.shift_x = 0; - info->hash_key.shift_y = 0; - } - } - } + // rearrange text in several lines + wrap_lines_smart(render_priv, max_text_width); - // convert glyphs to bitmaps - for (i = 0; i < text_info.length; ++i) - get_bitmap_glyph(text_info.glyphs + i); + // align text + last_break = -1; + for (i = 1; i < text_info->length + 1; ++i) { // (text_info->length + 1) is the end of the last line + if ((i == text_info->length) + || text_info->glyphs[i].linebreak) { + double width, shift = 0; + glyph_info_t *first_glyph = + text_info->glyphs + last_break + 1; + glyph_info_t *last_glyph = text_info->glyphs + i - 1; - memset(event_images, 0, sizeof(*event_images)); - event_images->top = device_y - d6_to_int(text_info.lines[0].asc); - event_images->height = d6_to_int(text_info.height); - event_images->detect_collisions = render_context.detect_collisions; - event_images->shift_direction = (valign == VALIGN_TOP) ? 1 : -1; - event_images->event = event; - event_images->imgs = render_text(&text_info, device_x, device_y); + while ((last_glyph > first_glyph) + && ((last_glyph->symbol == '\n') + || (last_glyph->symbol == 0))) + last_glyph--; - free_render_context(); - - return 0; + width = d6_to_double( + last_glyph->pos.x + last_glyph->advance.x - + first_glyph->pos.x); + if (halign == HALIGN_LEFT) { // left aligned, no action + shift = 0; + } else if (halign == HALIGN_RIGHT) { // right aligned + shift = max_text_width - width; + } else if (halign == HALIGN_CENTER) { // centered + shift = (max_text_width - width) / 2.0; + } + for (j = last_break + 1; j < i; ++j) { + text_info->glyphs[j].pos.x += double_to_d6(shift); + } + last_break = i - 1; + } + } + } else { // render_priv->state.evt_type == EVENT_HSCROLL + measure_text(render_priv); + } + + // determing text bounding box + compute_string_bbox(text_info, &bbox); + + // determine device coordinates for text + + // x coordinate for everything except positioned events + if (render_priv->state.evt_type == EVENT_NORMAL || + render_priv->state.evt_type == EVENT_VSCROLL) { + device_x = x2scr(render_priv, MarginL); + } else if (render_priv->state.evt_type == EVENT_HSCROLL) { + if (render_priv->state.scroll_direction == SCROLL_RL) + device_x = + x2scr(render_priv, + render_priv->track->PlayResX - + render_priv->state.scroll_shift); + else if (render_priv->state.scroll_direction == SCROLL_LR) + device_x = + x2scr(render_priv, + render_priv->state.scroll_shift) - (bbox.xMax - + bbox.xMin); + } + // y coordinate for everything except positioned events + if (render_priv->state.evt_type == EVENT_NORMAL || + render_priv->state.evt_type == EVENT_HSCROLL) { + if (valign == VALIGN_TOP) { // toptitle + device_y = + y2scr_top(render_priv, + MarginV) + text_info->lines[0].asc; + } else if (valign == VALIGN_CENTER) { // midtitle + double scr_y = + y2scr(render_priv, render_priv->track->PlayResY / 2.0); + device_y = scr_y - (bbox.yMax - bbox.yMin) / 2.0; + } else { // subtitle + double scr_y; + if (valign != VALIGN_SUB) + ass_msg(MSGL_V, + "Invalid valign, supposing 0 (subtitle)\n"); + scr_y = + y2scr_sub(render_priv, + render_priv->track->PlayResY - MarginV); + device_y = scr_y; + device_y -= text_info->height; + device_y += text_info->lines[0].asc; + } + } else if (render_priv->state.evt_type == EVENT_VSCROLL) { + if (render_priv->state.scroll_direction == SCROLL_TB) + device_y = + y2scr(render_priv, + render_priv->state.clip_y0 + + render_priv->state.scroll_shift) - (bbox.yMax - + bbox.yMin); + else if (render_priv->state.scroll_direction == SCROLL_BT) + device_y = + y2scr(render_priv, + render_priv->state.clip_y1 - + render_priv->state.scroll_shift); + } + // positioned events are totally different + if (render_priv->state.evt_type == EVENT_POSITIONED) { + double base_x = 0; + double base_y = 0; + ass_msg(MSGL_DBG2, "positioned event at %f, %f\n", + render_priv->state.pos_x, render_priv->state.pos_y); + get_base_point(&bbox, alignment, &base_x, &base_y); + device_x = + x2scr_pos(render_priv, render_priv->state.pos_x) - base_x; + device_y = + y2scr_pos(render_priv, render_priv->state.pos_y) - base_y; + } + // fix clip coordinates (they depend on alignment) + if (render_priv->state.evt_type == EVENT_NORMAL || + render_priv->state.evt_type == EVENT_HSCROLL || + render_priv->state.evt_type == EVENT_VSCROLL) { + render_priv->state.clip_x0 = + x2scr(render_priv, render_priv->state.clip_x0); + render_priv->state.clip_x1 = + x2scr(render_priv, render_priv->state.clip_x1); + if (valign == VALIGN_TOP) { + render_priv->state.clip_y0 = + y2scr_top(render_priv, render_priv->state.clip_y0); + render_priv->state.clip_y1 = + y2scr_top(render_priv, render_priv->state.clip_y1); + } else if (valign == VALIGN_CENTER) { + render_priv->state.clip_y0 = + y2scr(render_priv, render_priv->state.clip_y0); + render_priv->state.clip_y1 = + y2scr(render_priv, render_priv->state.clip_y1); + } else if (valign == VALIGN_SUB) { + render_priv->state.clip_y0 = + y2scr_sub(render_priv, render_priv->state.clip_y0); + render_priv->state.clip_y1 = + y2scr_sub(render_priv, render_priv->state.clip_y1); + } + } else if (render_priv->state.evt_type == EVENT_POSITIONED) { + render_priv->state.clip_x0 = + x2scr_pos(render_priv, render_priv->state.clip_x0); + render_priv->state.clip_x1 = + x2scr_pos(render_priv, render_priv->state.clip_x1); + render_priv->state.clip_y0 = + y2scr_pos(render_priv, render_priv->state.clip_y0); + render_priv->state.clip_y1 = + y2scr_pos(render_priv, render_priv->state.clip_y1); + } + // calculate rotation parameters + { + double_vector_t center; + + if (render_priv->state.have_origin) { + center.x = x2scr(render_priv, render_priv->state.org_x); + center.y = y2scr(render_priv, render_priv->state.org_y); + } else { + double bx = 0., by = 0.; + get_base_point(&bbox, alignment, &bx, &by); + center.x = device_x + bx; + center.y = device_y + by; + } + + for (i = 0; i < text_info->length; ++i) { + glyph_info_t *info = text_info->glyphs + i; + + if (info->hash_key.frx || info->hash_key.fry + || info->hash_key.frz || info->hash_key.fax + || info->hash_key.fay) { + info->hash_key.shift_x = info->pos.x + double_to_d6(device_x - center.x); + info->hash_key.shift_y = + -(info->pos.y + double_to_d6(device_y - center.y)); + } else { + info->hash_key.shift_x = 0; + info->hash_key.shift_y = 0; + } + } + } + + // convert glyphs to bitmaps + for (i = 0; i < text_info->length; ++i) { + glyph_info_t *g = text_info->glyphs + i; + g->hash_key.advance.x = + double_to_d6(device_x - (int) device_x + + d6_to_double(g->pos.x & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY; + g->hash_key.advance.y = + double_to_d6(device_y - (int) device_y + + d6_to_double(g->pos.y & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY; + get_bitmap_glyph(render_priv, text_info->glyphs + i); + } + + memset(event_images, 0, sizeof(*event_images)); + event_images->top = device_y - text_info->lines[0].asc; + event_images->height = text_info->height; + event_images->detect_collisions = render_priv->state.detect_collisions; + event_images->shift_direction = (valign == VALIGN_TOP) ? 1 : -1; + event_images->event = event; + event_images->imgs = render_text(render_priv, (int) device_x, (int) device_y); + + free_render_context(render_priv); + + return 0; } /** * \brief deallocate image list * \param img list pointer */ -static void ass_free_images(ass_image_t* img) +static void ass_free_images(ass_image_t *img) { - while (img) { - ass_image_t* next = img->next; - free(img); - img = next; - } + while (img) { + ass_image_t *next = img->next; + free(img); + img = next; + } } -static void ass_reconfigure(ass_renderer_t* priv) +static void ass_reconfigure(ass_renderer_t *priv) { - priv->render_id = ++last_render_id; - ass_glyph_cache_reset(); - ass_bitmap_cache_reset(); - ass_composite_cache_reset(); - ass_free_images(priv->prev_images_root); - priv->prev_images_root = 0; + priv->render_id = ++last_render_id; + priv->cache.glyph_cache = + ass_glyph_cache_reset(priv->cache.glyph_cache); + priv->cache.bitmap_cache = + ass_bitmap_cache_reset(priv->cache.bitmap_cache); + priv->cache.composite_cache = + ass_composite_cache_reset(priv->cache.composite_cache); + ass_free_images(priv->prev_images_root); + priv->prev_images_root = 0; } -void ass_set_frame_size(ass_renderer_t* priv, int w, int h) +void ass_set_frame_size(ass_renderer_t *priv, int w, int h) { - if (priv->settings.frame_width != w || priv->settings.frame_height != h) { - priv->settings.frame_width = w; - priv->settings.frame_height = h; - if (priv->settings.aspect == 0.) - priv->settings.aspect = ((double)w) / h; - ass_reconfigure(priv); - } + if (priv->settings.frame_width != w || priv->settings.frame_height != h) { + priv->settings.frame_width = w; + priv->settings.frame_height = h; + if (priv->settings.aspect == 0.) { + priv->settings.aspect = ((double) w) / h; + priv->settings.pixel_ratio = ((double) w) / h; + } + ass_reconfigure(priv); + } } -void ass_set_margins(ass_renderer_t* priv, int t, int b, int l, int r) +void ass_set_margins(ass_renderer_t *priv, int t, int b, int l, int r) { - if (priv->settings.left_margin != l || - priv->settings.right_margin != r || - priv->settings.top_margin != t || - priv->settings.bottom_margin != b) { - priv->settings.left_margin = l; - priv->settings.right_margin = r; - priv->settings.top_margin = t; - priv->settings.bottom_margin = b; - ass_reconfigure(priv); - } + if (priv->settings.left_margin != l || + priv->settings.right_margin != r || + priv->settings.top_margin != t + || priv->settings.bottom_margin != b) { + priv->settings.left_margin = l; + priv->settings.right_margin = r; + priv->settings.top_margin = t; + priv->settings.bottom_margin = b; + ass_reconfigure(priv); + } } -void ass_set_use_margins(ass_renderer_t* priv, int use) +void ass_set_use_margins(ass_renderer_t *priv, int use) { - priv->settings.use_margins = use; + priv->settings.use_margins = use; } -void ass_set_aspect_ratio(ass_renderer_t* priv, double ar) +void ass_set_aspect_ratio(ass_renderer_t *priv, double ar, double par) { - if (priv->settings.aspect != ar) { - priv->settings.aspect = ar; - ass_reconfigure(priv); - } + if (priv->settings.aspect != ar || priv->settings.pixel_ratio != par) { + priv->settings.aspect = ar; + priv->settings.pixel_ratio = par; + ass_reconfigure(priv); + } } -void ass_set_font_scale(ass_renderer_t* priv, double font_scale) +void ass_set_font_scale(ass_renderer_t *priv, double font_scale) { - if (priv->settings.font_size_coeff != font_scale) { - priv->settings.font_size_coeff = font_scale; - ass_reconfigure(priv); - } + if (priv->settings.font_size_coeff != font_scale) { + priv->settings.font_size_coeff = font_scale; + ass_reconfigure(priv); + } } -void ass_set_hinting(ass_renderer_t* priv, ass_hinting_t ht) +void ass_set_hinting(ass_renderer_t *priv, ass_hinting_t ht) { - if (priv->settings.hinting != ht) { - priv->settings.hinting = ht; - ass_reconfigure(priv); - } + if (priv->settings.hinting != ht) { + priv->settings.hinting = ht; + ass_reconfigure(priv); + } } -void ass_set_line_spacing(ass_renderer_t* priv, double line_spacing) +void ass_set_line_spacing(ass_renderer_t *priv, double line_spacing) { - priv->settings.line_spacing = line_spacing; + priv->settings.line_spacing = line_spacing; } -static int ass_set_fonts_(ass_renderer_t* priv, const char* default_font, const char* default_family, int fc) +int ass_set_fonts(ass_renderer_t *priv, const char *default_font, + const char *default_family, int fc, const char *config) { - if (priv->settings.default_font) - free(priv->settings.default_font); - if (priv->settings.default_family) - free(priv->settings.default_family); + if (priv->settings.default_font) + free(priv->settings.default_font); + if (priv->settings.default_family) + free(priv->settings.default_family); - priv->settings.default_font = default_font ? strdup(default_font) : 0; - priv->settings.default_family = default_family ? strdup(default_family) : 0; + free(priv->settings.default_font); + free(priv->settings.default_family); + priv->settings.default_font = default_font ? strdup(default_font) : 0; + priv->settings.default_family = + default_family ? strdup(default_family) : 0; - if (priv->fontconfig_priv) - fontconfig_done(priv->fontconfig_priv); - priv->fontconfig_priv = fontconfig_init(priv->library, priv->ftlibrary, default_family, default_font, fc); + if (priv->fontconfig_priv) + fontconfig_done(priv->fontconfig_priv); + priv->fontconfig_priv = + fontconfig_init(priv->library, priv->ftlibrary, default_family, + default_font, fc, config); - return !!priv->fontconfig_priv; -} - -int ass_set_fonts(ass_renderer_t* priv, const char* default_font, const char* default_family) -{ - return ass_set_fonts_(priv, default_font, default_family, 1); -} - -int ass_set_fonts_nofc(ass_renderer_t* priv, const char* default_font, const char* default_family) -{ - return ass_set_fonts_(priv, default_font, default_family, 0); + return !!priv->fontconfig_priv; } /** * \brief Start a new frame */ -static int ass_start_frame(ass_renderer_t *priv, ass_track_t* track, long long now) +static int +ass_start_frame(ass_renderer_t *render_priv, ass_track_t *track, + long long now) { - ass_renderer = priv; - global_settings = &priv->settings; + ass_settings_t *settings_priv = &render_priv->settings; - if (!priv->settings.frame_width && !priv->settings.frame_height) - return 1; // library not initialized + if (!render_priv->settings.frame_width + && !render_priv->settings.frame_height) + return 1; // library not initialized - if (track->n_events == 0) - return 1; // nothing to do - - frame_context.ass_priv = priv; - frame_context.width = global_settings->frame_width; - frame_context.height = global_settings->frame_height; - frame_context.orig_width = global_settings->frame_width - global_settings->left_margin - global_settings->right_margin; - frame_context.orig_height = global_settings->frame_height - global_settings->top_margin - global_settings->bottom_margin; - frame_context.orig_width_nocrop = global_settings->frame_width - - FFMAX(global_settings->left_margin, 0) - - FFMAX(global_settings->right_margin, 0); - frame_context.orig_height_nocrop = global_settings->frame_height - - FFMAX(global_settings->top_margin, 0) - - FFMAX(global_settings->bottom_margin, 0); - frame_context.track = track; - frame_context.time = now; + if (track->n_events == 0) + return 1; // nothing to do - ass_lazy_track_init(); - - frame_context.font_scale = global_settings->font_size_coeff * - frame_context.orig_height / frame_context.track->PlayResY; - if (frame_context.track->ScaledBorderAndShadow) - frame_context.border_scale = ((double)frame_context.orig_height) / frame_context.track->PlayResY; - else - frame_context.border_scale = 1.; + render_priv->width = settings_priv->frame_width; + render_priv->height = settings_priv->frame_height; + render_priv->orig_width = + settings_priv->frame_width - settings_priv->left_margin - + settings_priv->right_margin; + render_priv->orig_height = + settings_priv->frame_height - settings_priv->top_margin - + settings_priv->bottom_margin; + render_priv->orig_width_nocrop = + settings_priv->frame_width - FFMAX(settings_priv->left_margin, + 0) - + FFMAX(settings_priv->right_margin, 0); + render_priv->orig_height_nocrop = + settings_priv->frame_height - FFMAX(settings_priv->top_margin, + 0) - + FFMAX(settings_priv->bottom_margin, 0); + render_priv->track = track; + render_priv->time = now; - frame_context.font_scale_x = 1.; + ass_lazy_track_init(render_priv); - priv->prev_images_root = priv->images_root; - priv->images_root = 0; + render_priv->font_scale = settings_priv->font_size_coeff * + render_priv->orig_height / render_priv->track->PlayResY; + if (render_priv->track->ScaledBorderAndShadow) + render_priv->border_scale = + ((double) render_priv->orig_height) / + render_priv->track->PlayResY; + else + render_priv->border_scale = 1.; - return 0; + // PAR correction + render_priv->font_scale_x = render_priv->settings.aspect / + render_priv->settings.pixel_ratio; + + render_priv->prev_images_root = render_priv->images_root; + render_priv->images_root = 0; + + return 0; } -static int cmp_event_layer(const void* p1, const void* p2) +static int cmp_event_layer(const void *p1, const void *p2) { - ass_event_t* e1 = ((event_images_t*)p1)->event; - ass_event_t* e2 = ((event_images_t*)p2)->event; - if (e1->Layer < e2->Layer) - return -1; - if (e1->Layer > e2->Layer) - return 1; - if (e1->ReadOrder < e2->ReadOrder) - return -1; - if (e1->ReadOrder > e2->ReadOrder) - return 1; - return 0; + ass_event_t *e1 = ((event_images_t *) p1)->event; + ass_event_t *e2 = ((event_images_t *) p2)->event; + if (e1->Layer < e2->Layer) + return -1; + if (e1->Layer > e2->Layer) + return 1; + if (e1->ReadOrder < e2->ReadOrder) + return -1; + if (e1->ReadOrder > e2->ReadOrder) + return 1; + return 0; } #define MAX_EVENTS 100 -static render_priv_t* get_render_priv(ass_event_t* event) +static render_priv_t *get_render_priv(ass_renderer_t *render_priv, + ass_event_t *event) { - if (!event->render_priv) - event->render_priv = calloc(1, sizeof(render_priv_t)); - // FIXME: check render_id - if (ass_renderer->render_id != event->render_priv->render_id) { - memset(event->render_priv, 0, sizeof(render_priv_t)); - event->render_priv->render_id = ass_renderer->render_id; - } - return event->render_priv; + if (!event->render_priv) + event->render_priv = calloc(1, sizeof(render_priv_t)); + // FIXME: check render_id + if (render_priv->render_id != event->render_priv->render_id) { + memset(event->render_priv, 0, sizeof(render_priv_t)); + event->render_priv->render_id = render_priv->render_id; + } + return event->render_priv; } typedef struct segment_s { - int a, b; // top and height + int a, b; // top and height } segment_t; -static int overlap(segment_t* s1, segment_t* s2) +static int overlap(segment_t *s1, segment_t *s2) { - if (s1->a >= s2->b || s2->a >= s1->b) - return 0; - return 1; + if (s1->a >= s2->b || s2->a >= s1->b) + return 0; + return 1; } -static int cmp_segment(const void* p1, const void* p2) +static int cmp_segment(const void *p1, const void *p2) { - return ((segment_t*)p1)->a - ((segment_t*)p2)->a; + return ((segment_t *) p1)->a - ((segment_t *) p2)->a; } -static void shift_event(event_images_t* ei, int shift) +static void +shift_event(ass_renderer_t *render_priv, event_images_t *ei, int shift) { - ass_image_t* cur = ei->imgs; - while (cur) { - cur->dst_y += shift; - // clip top and bottom - if (cur->dst_y < 0) { - int clip = - cur->dst_y; - cur->h -= clip; - cur->bitmap += clip * cur->stride; - cur->dst_y = 0; - } - if (cur->dst_y + cur->h >= frame_context.height) { - int clip = cur->dst_y + cur->h - frame_context.height; - cur->h -= clip; - } - if (cur->h <= 0) { - cur->h = 0; - cur->dst_y = 0; - } - cur = cur->next; - } - ei->top += shift; + ass_image_t *cur = ei->imgs; + while (cur) { + cur->dst_y += shift; + // clip top and bottom + if (cur->dst_y < 0) { + int clip = -cur->dst_y; + cur->h -= clip; + cur->bitmap += clip * cur->stride; + cur->dst_y = 0; + } + if (cur->dst_y + cur->h >= render_priv->height) { + int clip = cur->dst_y + cur->h - render_priv->height; + cur->h -= clip; + } + if (cur->h <= 0) { + cur->h = 0; + cur->dst_y = 0; + } + cur = cur->next; + } + ei->top += shift; } // dir: 1 - move down // -1 - move up -static int fit_segment(segment_t* s, segment_t* fixed, int* cnt, int dir) +static int fit_segment(segment_t *s, segment_t *fixed, int *cnt, int dir) { - int i; - int shift = 0; + int i; + int shift = 0; - if (dir == 1) // move down - for (i = 0; i < *cnt; ++i) { - if (s->b + shift <= fixed[i].a || s->a + shift >= fixed[i].b) - continue; - shift = fixed[i].b - s->a; - } - else // dir == -1, move up - for (i = *cnt-1; i >= 0; --i) { - if (s->b + shift <= fixed[i].a || s->a + shift >= fixed[i].b) - continue; - shift = fixed[i].a - s->b; - } + if (dir == 1) // move down + for (i = 0; i < *cnt; ++i) { + if (s->b + shift <= fixed[i].a || s->a + shift >= fixed[i].b) + continue; + shift = fixed[i].b - s->a; + } else // dir == -1, move up + for (i = *cnt - 1; i >= 0; --i) { + if (s->b + shift <= fixed[i].a || s->a + shift >= fixed[i].b) + continue; + shift = fixed[i].a - s->b; + } - fixed[*cnt].a = s->a + shift; - fixed[*cnt].b = s->b + shift; - (*cnt)++; - qsort(fixed, *cnt, sizeof(segment_t), cmp_segment); - - return shift; + fixed[*cnt].a = s->a + shift; + fixed[*cnt].b = s->b + shift; + (*cnt)++; + qsort(fixed, *cnt, sizeof(segment_t), cmp_segment); + + return shift; } -static void fix_collisions(event_images_t* imgs, int cnt) +static void +fix_collisions(ass_renderer_t *render_priv, event_images_t *imgs, int cnt) { - segment_t used[MAX_EVENTS]; - int cnt_used = 0; - int i, j; + segment_t used[MAX_EVENTS]; + int cnt_used = 0; + int i, j; - // fill used[] with fixed events - for (i = 0; i < cnt; ++i) { - render_priv_t* priv; - if (!imgs[i].detect_collisions) continue; - priv = get_render_priv(imgs[i].event); - if (priv->height > 0) { // it's a fixed event - segment_t s; - s.a = priv->top; - s.b = priv->top + priv->height; - if (priv->height != imgs[i].height) { // no, it's not - mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_EventHeightHasChanged); - priv->top = 0; - priv->height = 0; - } - for (j = 0; j < cnt_used; ++j) - if (overlap(&s, used + j)) { // no, it's not - priv->top = 0; - priv->height = 0; - } - if (priv->height > 0) { // still a fixed event - used[cnt_used].a = priv->top; - used[cnt_used].b = priv->top + priv->height; - cnt_used ++; - shift_event(imgs + i, priv->top - imgs[i].top); - } - } - } - qsort(used, cnt_used, sizeof(segment_t), cmp_segment); + // fill used[] with fixed events + for (i = 0; i < cnt; ++i) { + render_priv_t *priv; + if (!imgs[i].detect_collisions) + continue; + priv = get_render_priv(render_priv, imgs[i].event); + if (priv->height > 0) { // it's a fixed event + segment_t s; + s.a = priv->top; + s.b = priv->top + priv->height; + if (priv->height != imgs[i].height) { // no, it's not + ass_msg(MSGL_WARN, + MSGTR_LIBASS_EventHeightHasChanged); + priv->top = 0; + priv->height = 0; + } + for (j = 0; j < cnt_used; ++j) + if (overlap(&s, used + j)) { // no, it's not + priv->top = 0; + priv->height = 0; + } + if (priv->height > 0) { // still a fixed event + used[cnt_used].a = priv->top; + used[cnt_used].b = priv->top + priv->height; + cnt_used++; + shift_event(render_priv, imgs + i, priv->top - imgs[i].top); + } + } + } + qsort(used, cnt_used, sizeof(segment_t), cmp_segment); - // try to fit other events in free spaces - for (i = 0; i < cnt; ++i) { - render_priv_t* priv; - if (!imgs[i].detect_collisions) continue; - priv = get_render_priv(imgs[i].event); - if (priv->height == 0) { // not a fixed event - int shift; - segment_t s; - s.a = imgs[i].top; - s.b = imgs[i].top + imgs[i].height; - shift = fit_segment(&s, used, &cnt_used, imgs[i].shift_direction); - if (shift) shift_event(imgs + i, shift); - // make it fixed - priv->top = imgs[i].top; - priv->height = imgs[i].height; - } - - } + // try to fit other events in free spaces + for (i = 0; i < cnt; ++i) { + render_priv_t *priv; + if (!imgs[i].detect_collisions) + continue; + priv = get_render_priv(render_priv, imgs[i].event); + if (priv->height == 0) { // not a fixed event + int shift; + segment_t s; + s.a = imgs[i].top; + s.b = imgs[i].top + imgs[i].height; + shift = + fit_segment(&s, used, &cnt_used, imgs[i].shift_direction); + if (shift) + shift_event(render_priv, imgs + i, shift); + // make it fixed + priv->top = imgs[i].top; + priv->height = imgs[i].height; + } + + } } /** @@ -2500,15 +3227,21 @@ static void fix_collisions(event_images_t* imgs, int cnt) */ static int ass_image_compare(ass_image_t *i1, ass_image_t *i2) { - if (i1->w != i2->w) return 2; - if (i1->h != i2->h) return 2; - if (i1->stride != i2->stride) return 2; - if (i1->color != i2->color) return 2; - if (i1->bitmap != i2->bitmap) - return 2; - if (i1->dst_x != i2->dst_x) return 1; - if (i1->dst_y != i2->dst_y) return 1; - return 0; + if (i1->w != i2->w) + return 2; + if (i1->h != i2->h) + return 2; + if (i1->stride != i2->stride) + return 2; + if (i1->color != i2->color) + return 2; + if (i1->bitmap != i2->bitmap) + return 2; + if (i1->dst_x != i2->dst_x) + return 1; + if (i1->dst_y != i2->dst_y) + return 1; + return 0; } /** @@ -2518,33 +3251,34 @@ static int ass_image_compare(ass_image_t *i1, ass_image_t *i2) */ static int ass_detect_change(ass_renderer_t *priv) { - ass_image_t* img, *img2; - int diff; + ass_image_t *img, *img2; + int diff; - img = priv->prev_images_root; - img2 = priv->images_root; - diff = 0; - while (img && diff < 2) { - ass_image_t* next, *next2; - next = img->next; - if (img2) { - int d = ass_image_compare(img, img2); - if (d > diff) diff = d; - next2 = img2->next; - } else { - // previous list is shorter - diff = 2; - break; - } - img = next; - img2 = next2; - } + img = priv->prev_images_root; + img2 = priv->images_root; + diff = 0; + while (img && diff < 2) { + ass_image_t *next, *next2; + next = img->next; + if (img2) { + int d = ass_image_compare(img, img2); + if (d > diff) + diff = d; + next2 = img2->next; + } else { + // previous list is shorter + diff = 2; + break; + } + img = next; + img2 = next2; + } - // is the previous list longer? - if (img2) - diff = 2; + // is the previous list longer? + if (img2) + diff = 2; - return diff; + return diff; } /** @@ -2556,62 +3290,66 @@ static int ass_detect_change(ass_renderer_t *priv) * 0 if identical, 1 if different positions, 2 if different content. * Can be NULL, in that case no detection is performed. */ -ass_image_t* ass_render_frame(ass_renderer_t *priv, ass_track_t* track, long long now, int* detect_change) +ass_image_t *ass_render_frame(ass_renderer_t *priv, ass_track_t *track, + long long now, int *detect_change) { - int i, cnt, rc; - event_images_t* last; - ass_image_t** tail; - - // init frame - rc = ass_start_frame(priv, track, now); - if (rc != 0) - return 0; + int i, cnt, rc; + event_images_t *last; + ass_image_t **tail; - // render events separately - cnt = 0; - for (i = 0; i < track->n_events; ++i) { - ass_event_t* event = track->events + i; - if ( (event->Start <= now) && (now < (event->Start + event->Duration)) ) { - if (cnt >= priv->eimg_size) { - priv->eimg_size += 100; - priv->eimg = realloc(priv->eimg, priv->eimg_size * sizeof(event_images_t)); - } - rc = ass_render_event(event, priv->eimg + cnt); - if (!rc) ++cnt; - } - } + // init frame + rc = ass_start_frame(priv, track, now); + if (rc != 0) + return 0; - // sort by layer - qsort(priv->eimg, cnt, sizeof(event_images_t), cmp_event_layer); + // render events separately + cnt = 0; + for (i = 0; i < track->n_events; ++i) { + ass_event_t *event = track->events + i; + if ((event->Start <= now) + && (now < (event->Start + event->Duration))) { + if (cnt >= priv->eimg_size) { + priv->eimg_size += 100; + priv->eimg = + realloc(priv->eimg, + priv->eimg_size * sizeof(event_images_t)); + } + rc = ass_render_event(priv, event, priv->eimg + cnt); + if (!rc) + ++cnt; + } + } - // call fix_collisions for each group of events with the same layer - last = priv->eimg; - for (i = 1; i < cnt; ++i) - if (last->event->Layer != priv->eimg[i].event->Layer) { - fix_collisions(last, priv->eimg + i - last); - last = priv->eimg + i; - } - if (cnt > 0) - fix_collisions(last, priv->eimg + cnt - last); + // sort by layer + qsort(priv->eimg, cnt, sizeof(event_images_t), cmp_event_layer); - // concat lists - tail = &ass_renderer->images_root; - for (i = 0; i < cnt; ++i) { - ass_image_t* cur = priv->eimg[i].imgs; - while (cur) { - *tail = cur; - tail = &cur->next; - cur = cur->next; - } - } + // call fix_collisions for each group of events with the same layer + last = priv->eimg; + for (i = 1; i < cnt; ++i) + if (last->event->Layer != priv->eimg[i].event->Layer) { + fix_collisions(priv, last, priv->eimg + i - last); + last = priv->eimg + i; + } + if (cnt > 0) + fix_collisions(priv, last, priv->eimg + cnt - last); - if (detect_change) - *detect_change = ass_detect_change(priv); - - // free the previous image list - ass_free_images(priv->prev_images_root); - priv->prev_images_root = 0; + // concat lists + tail = &priv->images_root; + for (i = 0; i < cnt; ++i) { + ass_image_t *cur = priv->eimg[i].imgs; + while (cur) { + *tail = cur; + tail = &cur->next; + cur = cur->next; + } + } - return ass_renderer->images_root; + if (detect_change) + *detect_change = ass_detect_change(priv); + + // free the previous image list + ass_free_images(priv->prev_images_root); + priv->prev_images_root = 0; + + return priv->images_root; } - diff --git a/aegisub/libass/ass_types.h b/aegisub/libass/ass_types.h index 01c36c574..cdea14ef4 100644 --- a/aegisub/libass/ass_types.h +++ b/aegisub/libass/ass_types.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 * @@ -34,31 +32,31 @@ /// ass Style: line typedef struct ass_style_s { - char* Name; - char* FontName; - double FontSize; - uint32_t PrimaryColour; - uint32_t SecondaryColour; - uint32_t OutlineColour; - uint32_t BackColour; - int Bold; - int Italic; - int Underline; - int StrikeOut; - double ScaleX; - double ScaleY; - double Spacing; - int Angle; - int BorderStyle; - double Outline; - double Shadow; - int Alignment; - int MarginL; - int MarginR; - int MarginV; + char *Name; + char *FontName; + double FontSize; + uint32_t PrimaryColour; + uint32_t SecondaryColour; + uint32_t OutlineColour; + uint32_t BackColour; + int Bold; + int Italic; + int Underline; + int StrikeOut; + double ScaleX; + double ScaleY; + double Spacing; + int Angle; + int BorderStyle; + double Outline; + double Shadow; + int Alignment; + int MarginL; + int MarginR; + int MarginV; // int AlphaLevel; - int Encoding; - int treat_fontname_as_pattern; + int Encoding; + int treat_fontname_as_pattern; } ass_style_t; typedef struct render_priv_s render_priv_t; @@ -66,20 +64,20 @@ typedef struct render_priv_s render_priv_t; /// ass_event_t corresponds to a single Dialogue line /// Text is stored as-is, style overrides will be parsed later typedef struct ass_event_s { - long long Start; // ms - long long Duration; // ms + long long Start; // ms + long long Duration; // ms - int ReadOrder; - int Layer; - int Style; - char* Name; - int MarginL; - int MarginR; - int MarginV; - char* Effect; - char* Text; + int ReadOrder; + int Layer; + int Style; + char *Name; + int MarginL; + int MarginR; + int MarginV; + char *Effect; + char *Text; - render_priv_t* render_priv; + render_priv_t *render_priv; } ass_event_t; typedef struct parser_priv_s parser_priv_t; @@ -89,31 +87,32 @@ typedef struct ass_library_s ass_library_t; /// ass track represent either an external script or a matroska subtitle stream (no real difference between them) /// it can be used in rendering after the headers are parsed (i.e. events format line read) typedef struct ass_track_s { - int n_styles; // amount used - int max_styles; // amount allocated - int n_events; - int max_events; - ass_style_t* styles; // array of styles, max_styles length, n_styles used - ass_event_t* events; // the same as styles + int n_styles; // amount used + int max_styles; // amount allocated + int n_events; + int max_events; + ass_style_t *styles; // array of styles, max_styles length, n_styles used + ass_event_t *events; // the same as styles - char* style_format; // style format line (everything after "Format: ") - char* event_format; // event format line + char *style_format; // style format line (everything after "Format: ") + char *event_format; // event format line - enum {TRACK_TYPE_UNKNOWN = 0, TRACK_TYPE_ASS, TRACK_TYPE_SSA} track_type; - - // script header fields - int PlayResX; - int PlayResY; - double Timer; - int WrapStyle; - char ScaledBorderAndShadow; + enum { TRACK_TYPE_UNKNOWN = + 0, TRACK_TYPE_ASS, TRACK_TYPE_SSA } track_type; - - int default_style; // index of default style - char* name; // file name in case of external subs, 0 for streams + // script header fields + int PlayResX; + int PlayResY; + double Timer; + int WrapStyle; + char ScaledBorderAndShadow; - ass_library_t* library; - parser_priv_t* parser_priv; + + int default_style; // index of default style + char *name; // file name in case of external subs, 0 for streams + + ass_library_t *library; + parser_priv_t *parser_priv; } ass_track_t; -#endif /* LIBASS_TYPES_H */ +#endif /* LIBASS_TYPES_H */ diff --git a/aegisub/libass/ass_utils.c b/aegisub/libass/ass_utils.c index 7f728f1bb..5f632684c 100644 --- a/aegisub/libass/ass_utils.c +++ b/aegisub/libass/ass_utils.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 * @@ -27,109 +25,301 @@ #include #include FT_GLYPH_H -#include "mputils.h" #include "ass_utils.h" -int mystrtoi(char** p, int* res) +int mystrtoi(char **p, int *res) { - // NOTE: base argument is ignored, but not used in libass anyway - double temp_res; - char* start = *p; - temp_res = strtod(*p, p); - *res = (int) (temp_res + 0.5); - if (*p != start) return 1; - else return 0; + // NOTE: base argument is ignored, but not used in libass anyway + double temp_res; + char *start = *p; + temp_res = strtod(*p, p); + *res = (int) (temp_res + (temp_res > 0 ? 0.5 : -0.5)); + if (*p != start) + return 1; + else + return 0; } -int mystrtoll(char** p, long long* res) +int mystrtoll(char **p, long long *res) { - double temp_res; - char* start = *p; - temp_res = strtod(*p, p); - *res = (long long) (temp_res + 0.5); - if (*p != start) return 1; - else return 0; + double temp_res; + char *start = *p; + temp_res = strtod(*p, p); + *res = (int) (temp_res + (temp_res > 0 ? 0.5 : -0.5)); + if (*p != start) + return 1; + else + return 0; } -int mystrtou32(char** p, int base, uint32_t* res) +int mystrtou32(char **p, int base, uint32_t *res) { - char* start = *p; - *res = strtoll(*p, p, base); - if (*p != start) return 1; - else return 0; + char *start = *p; + *res = strtoll(*p, p, base); + if (*p != start) + return 1; + else + return 0; } -int mystrtod(char** p, double* res) +int mystrtod(char **p, double *res) { - char* start = *p; - *res = strtod(*p, p); - if (*p != start) return 1; - else return 0; + char *start = *p; + *res = strtod(*p, p); + if (*p != start) + return 1; + else + return 0; } -int strtocolor(char** q, uint32_t* res) +int strtocolor(char **q, uint32_t *res) { - uint32_t color = 0; - int result; - char* p = *q; - - if (*p == '&') ++p; - else mp_msg(MSGT_ASS, MSGL_DBG2, "suspicious color format: \"%s\"\n", p); - - if (*p == 'H' || *p == 'h') { - ++p; - result = mystrtou32(&p, 16, &color); - } else { - result = mystrtou32(&p, 0, &color); - } - - { - unsigned char* tmp = (unsigned char*)(&color); - unsigned char b; - b = tmp[0]; tmp[0] = tmp[3]; tmp[3] = b; - b = tmp[1]; tmp[1] = tmp[2]; tmp[2] = b; - } - if (*p == '&') ++p; - *q = p; + uint32_t color = 0; + int result; + char *p = *q; - *res = color; - return result; + if (*p == '&') + ++p; + else + ass_msg(MSGL_DBG2, "suspicious color format: \"%s\"\n", p); + + if (*p == 'H' || *p == 'h') { + ++p; + result = mystrtou32(&p, 16, &color); + } else { + result = mystrtou32(&p, 0, &color); + } + + { + unsigned char *tmp = (unsigned char *) (&color); + unsigned char b; + b = tmp[0]; + tmp[0] = tmp[3]; + tmp[3] = b; + b = tmp[1]; + tmp[1] = tmp[2]; + tmp[2] = b; + } + if (*p == '&') + ++p; + *q = p; + + *res = color; + return result; } // Return a boolean value for a string -char parse_bool(char* str) { - while (*str == ' ' || *str == '\t') - str++; - if (!strncasecmp(str, "yes", 3)) - return 1; - else if (strtol(str, NULL, 10) > 0) - return 1; - return 0; +char parse_bool(char *str) +{ + while (*str == ' ' || *str == '\t') + str++; + if (!strncasecmp(str, "yes", 3)) + return 1; + else if (strtol(str, NULL, 10) > 0) + return 1; + return 0; } -#if 0 -static void sprint_tag(uint32_t tag, char* dst) +void ass_msg(int lvl, char *fmt, ...) { - dst[0] = (tag >> 24) & 0xFF; - dst[1] = (tag >> 16) & 0xFF; - dst[2] = (tag >> 8) & 0xFF; - dst[3] = tag & 0xFF; - dst[4] = 0; + va_list va; + if (lvl > MSGL_INFO) + return; + va_start(va, fmt); + vprintf(fmt, va); + va_end(va); } -void dump_glyph(FT_Glyph g) +unsigned ass_utf8_get_char(char **str) { - char tag[5]; - int i; - FT_OutlineGlyph og = (FT_OutlineGlyph)g; - FT_Outline* o = &(og->outline); - sprint_tag(g->format, tag); - printf("glyph: %p \n", g); - printf("format: %s \n", tag); - printf("outline: %p \n", o); - printf("contours: %d, points: %d, points ptr: %p \n", o->n_contours, o->n_points, o->points); - for (i = 0; i < o->n_points; ++i) { - printf(" point %f, %f \n", d6_to_double(o->points[i].x), d6_to_double(o->points[i].y)); - } + uint8_t *strp = (uint8_t *) * str; + unsigned c = *strp++; + unsigned mask = 0x80; + int len = -1; + while (c & mask) { + mask >>= 1; + len++; + } + if (len <= 0 || len > 4) + goto no_utf8; + c &= mask - 1; + while ((*strp & 0xc0) == 0x80) { + if (len-- <= 0) + goto no_utf8; + c = (c << 6) | (*strp++ & 0x3f); + } + if (len) + goto no_utf8; + *str = (char *) strp; + return c; + + no_utf8: + strp = (uint8_t *) * str; + c = *strp++; + *str = (char *) strp; + return c; +} + +// gaussian blur +void ass_gauss_blur(unsigned char *buffer, + unsigned short *tmp2, + int width, int height, int stride, int *m2, int r, int mwidth) +{ + + int x, y; + + unsigned char *s = buffer; + unsigned short *t = tmp2 + 1; + for (y = 0; y < height; y++) { + memset(t - 1, 0, (width + 1) * sizeof(short)); + + for (x = 0; x < r; x++) { + const int src = s[x]; + if (src) { + register unsigned short *dstp = t + x - r; + int mx; + unsigned *m3 = (unsigned *) (m2 + src * mwidth); + for (mx = r - x; mx < mwidth; mx++) { + dstp[mx] += m3[mx]; + } + } + } + + for (; x < width - r; x++) { + const int src = s[x]; + if (src) { + register unsigned short *dstp = t + x - r; + int mx; + unsigned *m3 = (unsigned *) (m2 + src * mwidth); + for (mx = 0; mx < mwidth; mx++) { + dstp[mx] += m3[mx]; + } + } + } + + for (; x < width; x++) { + const int src = s[x]; + if (src) { + register unsigned short *dstp = t + x - r; + int mx; + const int x2 = r + width - x; + unsigned *m3 = (unsigned *) (m2 + src * mwidth); + for (mx = 0; mx < x2; mx++) { + dstp[mx] += m3[mx]; + } + } + } + + s += stride; + t += width + 1; + } + + t = tmp2; + for (x = 0; x < width; x++) { + for (y = 0; y < r; y++) { + unsigned short *srcp = t + y * (width + 1) + 1; + int src = *srcp; + if (src) { + register unsigned short *dstp = srcp - 1 + width + 1; + const int src2 = (src + 128) >> 8; + unsigned *m3 = (unsigned *) (m2 + src2 * mwidth); + + int mx; + *srcp = 128; + for (mx = r - 1; mx < mwidth; mx++) { + *dstp += m3[mx]; + dstp += width + 1; + } + } + } + for (; y < height - r; y++) { + unsigned short *srcp = t + y * (width + 1) + 1; + int src = *srcp; + if (src) { + register unsigned short *dstp = srcp - 1 - r * (width + 1); + const int src2 = (src + 128) >> 8; + unsigned *m3 = (unsigned *) (m2 + src2 * mwidth); + + int mx; + *srcp = 128; + for (mx = 0; mx < mwidth; mx++) { + *dstp += m3[mx]; + dstp += width + 1; + } + } + } + for (; y < height; y++) { + unsigned short *srcp = t + y * (width + 1) + 1; + int src = *srcp; + if (src) { + const int y2 = r + height - y; + register unsigned short *dstp = srcp - 1 - r * (width + 1); + const int src2 = (src + 128) >> 8; + unsigned *m3 = (unsigned *) (m2 + src2 * mwidth); + + int mx; + *srcp = 128; + for (mx = 0; mx < y2; mx++) { + *dstp += m3[mx]; + dstp += width + 1; + } + } + } + t++; + } + + t = tmp2; + s = buffer; + for (y = 0; y < height; y++) { + for (x = 0; x < width; x++) { + s[x] = t[x] >> 8; + } + s += stride; + t += width + 1; + } +} + +#ifdef CONFIG_ENCA +void *ass_guess_buffer_cp(unsigned char *buffer, int buflen, + char *preferred_language, char *fallback) +{ + const char **languages; + size_t langcnt; + EncaAnalyser analyser; + EncaEncoding encoding; + char *detected_sub_cp = NULL; + int i; + + languages = enca_get_languages(&langcnt); + ass_msg(MSGL_V, "ENCA supported languages: "); + for (i = 0; i < langcnt; i++) { + ass_msg(MSGL_V, "%s ", languages[i]); + } + ass_msg(MSGL_V, "\n"); + + for (i = 0; i < langcnt; i++) { + const char *tmp; + + if (strcasecmp(languages[i], preferred_language) != 0) + continue; + analyser = enca_analyser_alloc(languages[i]); + encoding = enca_analyse_const(analyser, buffer, buflen); + tmp = enca_charset_name(encoding.charset, ENCA_NAME_STYLE_ICONV); + if (tmp && encoding.charset != ENCA_CS_UNKNOWN) { + detected_sub_cp = strdup(tmp); + ass_msg(MSGL_INFO, "ENCA detected charset: %s\n", tmp); + } + enca_analyser_free(analyser); + } + + free(languages); + + if (!detected_sub_cp) { + detected_sub_cp = strdup(fallback); + ass_msg(MSGL_INFO, + "ENCA detection failed: fallback to %s\n", fallback); + } + + return detected_sub_cp; } #endif diff --git a/aegisub/libass/ass_utils.h b/aegisub/libass/ass_utils.h index 8c5f3e8f4..cb1fae7da 100644 --- a/aegisub/libass/ass_utils.h +++ b/aegisub/libass/ass_utils.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 * @@ -23,44 +21,108 @@ #ifndef LIBASS_UTILS_H #define LIBASS_UTILS_H +#include +#include #include +#include +#include +#include -int mystrtoi(char** p, int* res); -int mystrtoll(char** p, long long* res); -int mystrtou32(char** p, int base, uint32_t* res); -int mystrtod(char** p, double* res); -int strtocolor(char** q, uint32_t* res); -char parse_bool(char* str); +#ifdef CONFIG_ENCA +#include +#endif -static inline int d6_to_int(int x) { - return (x + 32) >> 6; +#include "help_mp.h" + +#define MSGL_FATAL 0 +#define MSGL_ERR 1 +#define MSGL_WARN 2 +#define MSGL_INFO 4 +#define MSGL_V 6 +#define MSGL_DBG2 7 + +#define FFMAX(a,b) ((a) > (b) ? (a) : (b)) +#define FFMIN(a,b) ((a) > (b) ? (b) : (a)) + +int mystrtoi(char **p, int *res); +int mystrtoll(char **p, long long *res); +int mystrtou32(char **p, int base, uint32_t *res); +int mystrtod(char **p, double *res); +int strtocolor(char **q, uint32_t *res); +char parse_bool(char *str); +unsigned ass_utf8_get_char(char **str); +void ass_msg(int lvl, char *fmt, ...); +void ass_gauss_blur(unsigned char *buffer, unsigned short *tmp2, + int width, int height, int stride, int *m2, + int r, int mwidth); +void *ass_guess_buffer_cp(unsigned char *buffer, int buflen, + char *preferred_language, char *fallback); + +static inline int d6_to_int(int x) +{ + return (x + 32) >> 6; } -static inline int d16_to_int(int x) { - return (x + 32768) >> 16; +static inline int d16_to_int(int x) +{ + return (x + 32768) >> 16; } -static inline int int_to_d6(int x) { - return x << 6; +static inline int int_to_d6(int x) +{ + return x << 6; } -static inline int int_to_d16(int x) { - return x << 16; +static inline int int_to_d16(int x) +{ + return x << 16; } -static inline int d16_to_d6(int x) { - return (x + 512) >> 10; +static inline int d16_to_d6(int x) +{ + return (x + 512) >> 10; } -static inline int d6_to_d16(int x) { - return x << 10; +static inline int d6_to_d16(int x) +{ + return x << 10; } -static inline double d6_to_double(int x) { - return x / 64.; +static inline double d6_to_double(int x) +{ + return x / 64.; } -static inline int double_to_d6(double x) { - return (int)(x * 64); +static inline int double_to_d6(double x) +{ + return (int) (x * 64); } -static inline double d16_to_double(int x) { - return ((double)x) / 0x10000; +static inline double d16_to_double(int x) +{ + return ((double) x) / 0x10000; } -static inline int double_to_d16(double x) { - return (int)(x * 0x10000); +static inline int double_to_d16(double x) +{ + return (int) (x * 0x10000); } -#endif /* LIBASS_UTILS_H */ +#define FNV1_32A_INIT (unsigned)0x811c9dc5 + +static inline unsigned fnv_32a_buf(void *buf, size_t len, unsigned hval) +{ + unsigned char *bp = buf; + unsigned char *be = bp + len; + while (bp < be) { + hval ^= (unsigned) *bp++; + hval += + (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + + (hval << 24); + } + return hval; +} +static inline unsigned fnv_32a_str(char *str, unsigned hval) +{ + unsigned char *s = (unsigned char *) str; + while (*s) { + hval ^= (unsigned) *s++; + hval += + (hval << 1) + (hval << 4) + (hval << 7) + (hval << 8) + + (hval << 24); + } + return hval; +} + +#endif /* LIBASS_UTILS_H */ diff --git a/aegisub/libass/help_mp.h b/aegisub/libass/help_mp.h index a13eaed1b..4e235f4c3 100644 --- a/aegisub/libass/help_mp.h +++ b/aegisub/libass/help_mp.h @@ -2,6 +2,7 @@ #define __LIBASS_HELP_MP_H__ #define MSGTR_LIBASS_FT_Glyph_To_BitmapError "[ass] FT_Glyph_To_Bitmap error %d \n" #define MSGTR_LIBASS_UnsupportedPixelMode "[ass] Unsupported pixel mode: %d\n" +#define MSGTR_LIBASS_GlyphBBoxTooLarge "[ass] Glyph bounding box too large: %dx%dpx\n" #define MSGTR_LIBASS_NoStyleNamedXFoundUsingY "[ass] [%p] Warning: no style named '%s' found, using '%s'\n" #define MSGTR_LIBASS_BadTimestamp "[ass] bad timestamp\n" #define MSGTR_LIBASS_BadEncodedDataSize "[ass] bad encoded data size\n" @@ -19,7 +20,7 @@ #define MSGTR_LIBASS_NotADirectory "[ass] Not a directory: %s\n" #define MSGTR_LIBASS_TooManyFonts "[ass] Too many fonts\n" #define MSGTR_LIBASS_ErrorOpeningFont "[ass] Error opening font: %s, %d\n" -#define MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne "[ass] fontconfig: Selected font family is not the requested one: '%s' != '%s'\n" +#define MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne "[ass] fontconfig: Selected font is not the requested one: '%s' != '%s'\n" #define MSGTR_LIBASS_UsingDefaultFontFamily "[ass] fontconfig_select: Using default font family: (%s, %d, %d) -> %s, %d\n" #define MSGTR_LIBASS_UsingDefaultFont "[ass] fontconfig_select: Using default font: (%s, %d, %d) -> %s, %d\n" #define MSGTR_LIBASS_UsingArialFontFamily "[ass] fontconfig_select: Using 'Arial' font family: (%s, %d, %d) -> %s, %d\n" @@ -46,11 +47,9 @@ #define MSGTR_LIBASS_EmptyEvent "[ass] Empty event!\n" #define MSGTR_LIBASS_MAX_GLYPHS_Reached "[ass] MAX_GLYPHS reached: event %d, start = %llu, duration = %llu\n Text = %s\n" #define MSGTR_LIBASS_EventHeightHasChanged "[ass] Warning! Event height has changed! \n" -#define MSGTR_LIBASS_GlyphNotFoundReselectingFont "[ass] Glyph 0x%X not found, reselecting font for (%s, %d, %d)\n" +#define MSGTR_LIBASS_GlyphNotFoundReselectingFont "[ass] Glyph 0x%X not found, selecting one more font for (%s, %d, %d)\n" #define MSGTR_LIBASS_GlyphNotFound "[ass] Glyph 0x%X not found in font for (%s, %d, %d)\n" #define MSGTR_LIBASS_ErrorOpeningMemoryFont "[ass] Error opening memory font: %s\n" #define MSGTR_LIBASS_NoCharmaps "[ass] font face with no charmaps\n" #define MSGTR_LIBASS_NoCharmapAutodetected "[ass] no charmap autodetected, trying the first one\n" -#define MSGTR_LIBASS_GlyphBBoxTooLarge "[ass] Glyph bounding box too large: %dx%dpx\n" #endif - diff --git a/aegisub/src/subtitles_provider_libass.cpp b/aegisub/src/subtitles_provider_libass.cpp index a924d12f8..76a4dcf0a 100644 --- a/aegisub/src/subtitles_provider_libass.cpp +++ b/aegisub/src/subtitles_provider_libass.cpp @@ -73,7 +73,7 @@ LibassSubtitlesProvider::LibassSubtitlesProvider() { ass_renderer = ass_renderer_init(ass_library); if (!ass_renderer) throw _T("ass_renderer_init failed"); ass_set_font_scale(ass_renderer, 1.); - ass_set_fonts(ass_renderer, NULL, "Sans"); + ass_set_fonts(ass_renderer, NULL, "Sans", 1, NULL); }