diff --git a/aegisub/libass/Makefile.am b/aegisub/libass/Makefile.am index 9ac93bf4f..92a1e194b 100644 --- a/aegisub/libass/Makefile.am +++ b/aegisub/libass/Makefile.am @@ -15,7 +15,8 @@ libass_aegisub_a_SOURCES = \ ass_fontconfig.c \ ass_library.c \ ass_render.c \ - ass_utils.c + ass_utils.c \ + ass_parse.c libass_aegisub_a_SOURCES += \ *.h diff --git a/aegisub/libass/ass.c b/aegisub/libass/ass.c index 9eaa7b01d..057a6e349 100644 --- a/aegisub/libass/ass.c +++ b/aegisub/libass/ass.c @@ -38,11 +38,16 @@ #include "ass_utils.h" #include "ass_library.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 +} ParserState; -struct parser_priv_s { - parser_state_t state; +struct parser_priv { + ParserState state; char *fontname; char *fontdata; int fontdata_size; @@ -52,7 +57,7 @@ struct parser_priv_s { #define ASS_STYLES_ALLOC 20 #define ASS_EVENTS_ALLOC 200 -void ass_free_track(ass_track_t *track) +void ass_free_track(ASS_Track *track) { int i; @@ -77,12 +82,14 @@ void ass_free_track(ass_track_t *track) ass_free_event(track, i); free(track->events); } + free(track->name); + free(track); } /// \brief Allocate a new style struct /// \param track track /// \return style id -int ass_alloc_style(ass_track_t *track) +int ass_alloc_style(ASS_Track *track) { int sid; @@ -91,20 +98,20 @@ int ass_alloc_style(ass_track_t *track) 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); + (ASS_Style *) realloc(track->styles, + sizeof(ASS_Style) * + track->max_styles); } sid = track->n_styles++; - memset(track->styles + sid, 0, sizeof(ass_style_t)); + memset(track->styles + sid, 0, sizeof(ASS_Style)); return sid; } /// \brief Allocate a new event struct /// \param track track /// \return event id -int ass_alloc_event(ass_track_t *track) +int ass_alloc_event(ASS_Track *track) { int eid; @@ -113,19 +120,19 @@ int ass_alloc_event(ass_track_t *track) 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); + (ASS_Event *) realloc(track->events, + sizeof(ASS_Event) * + track->max_events); } eid = track->n_events++; - memset(track->events + eid, 0, sizeof(ass_event_t)); + memset(track->events + eid, 0, sizeof(ASS_Event)); return eid; } -void ass_free_event(ass_track_t *track, int eid) +void ass_free_event(ASS_Track *track, int eid) { - ass_event_t *event = track->events + eid; + ASS_Event *event = track->events + eid; if (event->Name) free(event->Name); if (event->Effect) @@ -136,9 +143,9 @@ void ass_free_event(ass_track_t *track, int eid) free(event->render_priv); } -void ass_free_style(ass_track_t *track, int sid) +void ass_free_style(ASS_Track *track, int sid) { - ass_style_t *style = track->styles + sid; + ASS_Style *style = track->styles + sid; if (style->Name) free(style->Name); if (style->FontName) @@ -171,7 +178,7 @@ 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) +static int lookup_style(ASS_Track *track, char *name) { int i; if (*name == '*') @@ -188,14 +195,14 @@ static int lookup_style(ass_track_t *track, char *name) return i; // use the first style } -static uint32_t string2color(ass_library_t *library, char *p) +static uint32_t string2color(ASS_Library *library, char *p) { uint32_t tmp; - (void) strtocolor(library, &p, &tmp); + (void) strtocolor(library, &p, &tmp, 0); return tmp; } -static long long string2timecode(ass_library_t *library, char *p) +static long long string2timecode(ASS_Library *library, char *p) { unsigned h, m, s, ms; long long tm; @@ -292,14 +299,14 @@ static char *next_token(char **str) * \param str string to parse, zero-terminated * \param n_ignored number of format options to skip at the beginning */ -static int process_event_tail(ass_track_t *track, ass_event_t *event, +static int process_event_tail(ASS_Track *track, ASS_Event *event, char *str, int n_ignored) { char *token; char *tname; char *p = str; int i; - ass_event_t *target = event; + ASS_Event *target = event; char *format = strdup(track->event_format); char *q = format; // format scanning pointer @@ -355,10 +362,10 @@ static int process_event_tail(ass_track_t *track, ass_event_t *event, * \param track track to apply overrides to * The format for overrides is [StyleName.]Field=Value */ -void ass_process_force_style(ass_track_t *track) +void ass_process_force_style(ASS_Track *track) { char **fs, *eq, *dt, *style, *tname, *token; - ass_style_t *target; + ASS_Style *target; int sid; char **list = track->library->style_overrides; @@ -434,7 +441,7 @@ void ass_process_force_style(ass_track_t *track) * \param str string to parse, zero-terminated * Allocates a new style struct. */ -static int process_style(ass_track_t *track, char *str) +static int process_style(ASS_Track *track, char *str) { char *token; @@ -443,8 +450,8 @@ static int process_style(ass_track_t *track, char *str) char *format; char *q; // format scanning pointer int sid; - ass_style_t *style; - ass_style_t *target; + ASS_Style *style; + ASS_Style *target; if (!track->style_format) { // no style format header @@ -472,7 +479,8 @@ static int process_style(ass_track_t *track, char *str) style = track->styles + sid; target = style; -// fill style with some default values + + // fill style with some default values style->ScaleX = 100.; style->ScaleY = 100.; @@ -480,8 +488,6 @@ static int process_style(ass_track_t *track, char *str) 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) @@ -537,7 +543,7 @@ static int process_style(ass_track_t *track, char *str) } -static int process_styles_line(ass_track_t *track, char *str) +static int process_styles_line(ASS_Track *track, char *str) { if (!strncmp(str, "Format:", 7)) { char *p = str + 7; @@ -553,7 +559,7 @@ static int process_styles_line(ass_track_t *track, char *str) return 0; } -static int process_info_line(ass_track_t *track, char *str) +static int process_info_line(ASS_Track *track, char *str) { if (!strncmp(str, "PlayResX:", 9)) { track->PlayResX = atoi(str + 9); @@ -569,7 +575,7 @@ static int process_info_line(ass_track_t *track, char *str) return 0; } -static void event_format_fallback(ass_track_t *track) +static void event_format_fallback(ASS_Track *track) { track->parser_priv->state = PST_EVENTS; if (track->track_type == TRACK_TYPE_SSA) @@ -582,7 +588,7 @@ static void event_format_fallback(ass_track_t *track) "No event format found, using fallback"); } -static int process_events_line(ass_track_t *track, char *str) +static int process_events_line(ASS_Track *track, char *str) { if (!strncmp(str, "Format:", 7)) { char *p = str + 7; @@ -594,7 +600,7 @@ static int process_events_line(ass_track_t *track, char *str) // They have slightly different format and are parsed in ass_process_chunk, // called directly from demuxer int eid; - ass_event_t *event; + ASS_Event *event; str += 9; skip_spaces(&str); @@ -634,7 +640,7 @@ static unsigned char *decode_chars(unsigned char c1, unsigned char c2, return dst; } -static int decode_font(ass_track_t *track) +static int decode_font(ASS_Track *track) { unsigned char *p; unsigned char *q; @@ -682,7 +688,7 @@ static int decode_font(ass_track_t *track) return 0; } -static int process_fonts_line(ass_track_t *track, char *str) +static int process_fonts_line(ASS_Track *track, char *str) { int len; @@ -728,7 +734,7 @@ static int process_fonts_line(ass_track_t *track, char *str) * \param track track * \param str string to parse, zero-terminated */ -static int process_line(ass_track_t *track, char *str) +static int process_line(ASS_Track *track, char *str) { if (!strncasecmp(str, "[Script Info]", 13)) { track->parser_priv->state = PST_INFO; @@ -769,7 +775,7 @@ static int process_line(ass_track_t *track, char *str) return 0; } -static int process_text(ass_track_t *track, char *str) +static int process_text(ASS_Track *track, char *str) { char *p = str; while (1) { @@ -802,7 +808,7 @@ static int process_text(ass_track_t *track, char *str) * \param data string to parse * \param size length of data */ -void ass_process_data(ass_track_t *track, char *data, int size) +void ass_process_data(ASS_Track *track, char *data, int size) { char *str = malloc(size + 1); @@ -821,7 +827,7 @@ void ass_process_data(ass_track_t *track, char *data, int size) * \param size length of data CodecPrivate section contains [Stream Info] and [V4+ Styles] ([V4 Styles] for SSA) sections */ -void ass_process_codec_private(ass_track_t *track, char *data, int size) +void ass_process_codec_private(ASS_Track *track, char *data, int size) { ass_process_data(track, data, size); @@ -833,7 +839,7 @@ void ass_process_codec_private(ass_track_t *track, char *data, int size) ass_process_force_style(track); } -static int check_duplicate_event(ass_track_t *track, int ReadOrder) +static int check_duplicate_event(ASS_Track *track, int ReadOrder) { int i; for (i = 0; i < track->n_events - 1; ++i) // ignoring last event, it is the one we are comparing with @@ -850,14 +856,14 @@ static int check_duplicate_event(ass_track_t *track, int ReadOrder) * \param timecode starting time of the event (milliseconds) * \param duration duration of the event (milliseconds) */ -void ass_process_chunk(ass_track_t *track, char *data, int size, +void ass_process_chunk(ASS_Track *track, char *data, int size, long long timecode, long long duration) { char *str; int eid; char *p; char *token; - ass_event_t *event; + ASS_Event *event; if (!track->event_format) { ass_msg(track->library, MSGL_WARN, "Event format header missing"); @@ -906,7 +912,7 @@ void ass_process_chunk(ass_track_t *track, char *data, int size, * \param size buffer size * \return a pointer to recoded buffer, caller is responsible for freeing it **/ -static char *sub_recode(ass_library_t *library, char *data, size_t size, +static char *sub_recode(ASS_Library *library, char *data, size_t size, char *codepage) { iconv_t icdsc; @@ -985,7 +991,7 @@ static char *sub_recode(ass_library_t *library, char *data, size_t size, * \param bufsize out: file size * \return pointer to file contents. Caller is responsible for its deallocation. */ -static char *read_file(ass_library_t *library, char *fname, size_t *bufsize) +static char *read_file(ASS_Library *library, char *fname, size_t *bufsize) { int res; long sz; @@ -1044,9 +1050,9 @@ static char *read_file(ass_library_t *library, char *fname, size_t *bufsize) /* * \param buf pointer to subtitle text in utf-8 */ -static ass_track_t *parse_memory(ass_library_t *library, char *buf) +static ASS_Track *parse_memory(ASS_Library *library, char *buf) { - ass_track_t *track; + ASS_Track *track; int i; track = ass_new_track(library); @@ -1080,10 +1086,10 @@ static ass_track_t *parse_memory(ass_library_t *library, char *buf) * \param codepage recode buffer contents from given codepage * \return newly allocated track */ -ass_track_t *ass_read_memory(ass_library_t *library, char *buf, - size_t bufsize, char *codepage) +ASS_Track *ass_read_memory(ASS_Library *library, char *buf, + size_t bufsize, char *codepage) { - ass_track_t *track; + ASS_Track *track; int need_free = 0; if (!buf) @@ -1109,7 +1115,7 @@ ass_track_t *ass_read_memory(ass_library_t *library, char *buf, return track; } -static char *read_file_recode(ass_library_t *library, char *fname, +static char *read_file_recode(ASS_Library *library, char *fname, char *codepage, size_t *size) { char *buf; @@ -1138,11 +1144,11 @@ static char *read_file_recode(ass_library_t *library, char *fname, * \param codepage recode buffer contents from given codepage * \return newly allocated track */ -ass_track_t *ass_read_file(ass_library_t *library, char *fname, - char *codepage) +ASS_Track *ass_read_file(ASS_Library *library, char *fname, + char *codepage) { char *buf; - ass_track_t *track; + ASS_Track *track; size_t bufsize; buf = read_file_recode(library, fname, codepage, &bufsize); @@ -1165,10 +1171,10 @@ ass_track_t *ass_read_file(ass_library_t *library, char *fname, /** * \brief read styles from file into already initialized track */ -int ass_read_styles(ass_track_t *track, char *fname, char *codepage) +int ass_read_styles(ASS_Track *track, char *fname, char *codepage) { char *buf; - parser_state_t old_state; + ParserState old_state; size_t sz; buf = read_file(track->library, fname, &sz); @@ -1193,7 +1199,7 @@ int ass_read_styles(ass_track_t *track, char *fname, char *codepage) return 0; } -long long ass_step_sub(ass_track_t *track, long long now, int movement) +long long ass_step_sub(ASS_Track *track, long long now, int movement) { int i; @@ -1225,11 +1231,11 @@ long long ass_step_sub(ass_track_t *track, long long now, int movement) return ((long long) track->events[i].Start) - now; } -ass_track_t *ass_new_track(ass_library_t *library) +ASS_Track *ass_new_track(ASS_Library *library) { - ass_track_t *track = calloc(1, sizeof(ass_track_t)); + ASS_Track *track = calloc(1, sizeof(ASS_Track)); track->library = library; track->ScaledBorderAndShadow = 1; - track->parser_priv = calloc(1, sizeof(parser_priv_t)); + track->parser_priv = calloc(1, sizeof(ASS_ParserPriv)); return track; } diff --git a/aegisub/libass/ass.h b/aegisub/libass/ass.h index b92960a15..908b199f2 100644 --- a/aegisub/libass/ass.h +++ b/aegisub/libass/ass.h @@ -25,41 +25,54 @@ #include #include "ass_types.h" -#define LIBASS_VERSION 0x00907000 +#define LIBASS_VERSION 0x00907010 -/* Libass renderer object. Contents are private. */ -typedef struct ass_renderer_s ass_renderer_t; - -/* A linked list of images produced by ass renderer. */ -typedef struct ass_image_s { +/* + * A linked list of images produced by an ass renderer. + * + * These images have to be rendered in-order for the correct screen + * composition. The libass renderer clips these bitmaps to the frame size. + * w/h can be zero, in this case the bitmap should not be rendered at all. + * The last bitmap row is not guaranteed to be padded up to stride size, + * e.g. in the worst case a bitmap has the size stride * (h - 1) + w. + */ +typedef struct ass_image { int w, h; // Bitmap width/height int stride; // Bitmap stride unsigned char *bitmap; // 1bpp stride*h alpha buffer + // Note: the last row may not be padded to + // bitmap stride! uint32_t color; // Bitmap color and alpha, RGBA int dst_x, dst_y; // Bitmap placement inside the video frame - struct ass_image_s *next; // Next image, or NULL -} ass_image_t; + struct ass_image *next; // Next image, or NULL +} ASS_Image; -/* Hinting type. */ +/* + * Hintint type. (see ass_set_hinting below) + * + * FreeType's native hinter is still buggy sometimes and it is recommended + * to use the light autohinter, ASS_HINTING_LIGHT, instead. For best + * compatibility with problematic fonts, disable hinting. + */ typedef enum { ASS_HINTING_NONE = 0, ASS_HINTING_LIGHT, ASS_HINTING_NORMAL, ASS_HINTING_NATIVE -} ass_hinting_t; +} ASS_Hinting; /** * \brief Initialize the library. * \return library handle or NULL if failed */ -ass_library_t *ass_library_init(void); +ASS_Library *ass_library_init(void); /** * \brief Finalize the library * \param priv library handle */ -void ass_library_done(ass_library_t *); +void ass_library_done(ASS_Library *priv); /** * \brief Set private font directory. @@ -68,14 +81,14 @@ void ass_library_done(ass_library_t *); * \param priv library handle * \param fonts_dir private directory for font extraction */ -void ass_set_fonts_dir(ass_library_t *priv, const char *fonts_dir); +void ass_set_fonts_dir(ASS_Library *priv, const char *fonts_dir); /** * \brief Whether fonts should be extracted from track data. * \param priv library handle * \param extract whether to extract fonts */ -void ass_set_extract_fonts(ass_library_t *priv, int extract); +void ass_set_extract_fonts(ASS_Library *priv, int extract); /** * \brief Register style overrides with a library instance. @@ -86,13 +99,13 @@ void ass_set_extract_fonts(ass_library_t *priv, int extract); * \param priv library handle * \param list NULL-terminated list of strings */ -void ass_set_style_overrides(ass_library_t *priv, char **list); +void ass_set_style_overrides(ASS_Library *priv, char **list); /** * \brief Explicitly process style overrides for a track. * \param track track handle */ -void ass_process_force_style(ass_track_t *track); +void ass_process_force_style(ASS_Track *track); /** * \brief Register a callback for debug/info messages. @@ -108,8 +121,8 @@ void ass_process_force_style(ass_track_t *track); * \param msg_cb pointer to callback function * \param data additional data, will be passed to callback */ -void ass_set_message_cb(ass_library_t *priv, - void (*msg_cb)(int, char *, va_list *, void *), +void ass_set_message_cb(ASS_Library *priv, void (*msg_cb) + (int level, const char *fmt, va_list args, void *data), void *data); /** @@ -117,13 +130,13 @@ void ass_set_message_cb(ass_library_t *priv, * \param priv library handle * \return renderer handle or NULL if failed */ -ass_renderer_t *ass_renderer_init(ass_library_t *); +ASS_Renderer *ass_renderer_init(ASS_Library *); /** * \brief Finalize the renderer. * \param priv renderer handle */ -void ass_renderer_done(ass_renderer_t *priv); +void ass_renderer_done(ASS_Renderer *priv); /** * \brief Set the frame size in pixels, including margins. @@ -131,7 +144,7 @@ void ass_renderer_done(ass_renderer_t *priv); * \param w width * \param h height */ -void ass_set_frame_size(ass_renderer_t *priv, int w, int h); +void ass_set_frame_size(ASS_Renderer *priv, int w, int h); /** * \brief Set frame margins. These values may be negative if pan-and-scan @@ -142,43 +155,43 @@ void ass_set_frame_size(ass_renderer_t *priv, int w, int h); * \param l left margin * \param r right margin */ -void ass_set_margins(ass_renderer_t *priv, int t, int b, int l, int r); +void ass_set_margins(ASS_Renderer *priv, int t, int b, int l, int r); /** * \brief Whether margins should be used for placing regular events. * \param priv renderer handle * \param use whether to use the margins */ -void ass_set_use_margins(ass_renderer_t *priv, int use); +void ass_set_use_margins(ASS_Renderer *priv, int use); /** * \brief Set aspect ratio parameters. * \param priv renderer handle - * \param ar physical aspect ratio - * \param par pixel ratio, e.g. width / height of the video + * \param dar display aspect ratio (DAR), prescaled for output PAR + * \param sar storage aspect ratio (SAR) */ -void ass_set_aspect_ratio(ass_renderer_t *priv, double ar, double par); +void ass_set_aspect_ratio(ASS_Renderer *priv, double dar, double sar); /** * \brief Set a fixed font scaling factor. * \param priv renderer handle * \param font_scale scaling factor, default is 1.0 */ -void ass_set_font_scale(ass_renderer_t *priv, double font_scale); +void ass_set_font_scale(ASS_Renderer *priv, double font_scale); /** * \brief Set font hinting method. * \param priv renderer handle * \param ht hinting method */ -void ass_set_hinting(ass_renderer_t *priv, ass_hinting_t ht); +void ass_set_hinting(ASS_Renderer *priv, ASS_Hinting ht); /** * \brief Set line spacing. Will not be scaled with frame size. * \param priv renderer handle * \param line_spacing line spacing in pixels */ -void ass_set_line_spacing(ass_renderer_t *priv, double line_spacing); +void ass_set_line_spacing(ASS_Renderer *priv, double line_spacing); /** * \brief Set font lookup defaults. @@ -188,7 +201,7 @@ void ass_set_line_spacing(ass_renderer_t *priv, double line_spacing); * \param update whether fontconfig cache should be built/updated now. Only * relevant if fontconfig is used. */ -void ass_set_fonts(ass_renderer_t *priv, const char *default_font, +void ass_set_fonts(ASS_Renderer *priv, const char *default_font, const char *default_family, int fc, const char *config, int update); @@ -199,7 +212,7 @@ void ass_set_fonts(ass_renderer_t *priv, const char *default_font, * \param priv renderer handle * \return success */ -int ass_fonts_update(ass_renderer_t *priv); +int ass_fonts_update(ASS_Renderer *priv); /** * \brief Set hard cache limits. Do not set, or set to zero, for reasonable @@ -209,19 +222,19 @@ int ass_fonts_update(ass_renderer_t *priv); * \param glyph_max maximum number of cached glyphs * \param bitmap_max_size maximum bitmap cache size (in MB) */ -void ass_set_cache_limits(ass_renderer_t *priv, int glyph_max, +void ass_set_cache_limits(ASS_Renderer *priv, int glyph_max, int bitmap_max_size); /** - * \brief Render a frame, producing a list of ass_image_t. + * \brief Render a frame, producing a list of ASS_Image. * \param priv renderer handle * \param track subtitle track * \param now video timestamp in milliseconds * \param detect_change will be set to 1 if a change occured compared * to the last invocation */ -ass_image_t *ass_render_frame(ass_renderer_t *priv, ass_track_t *track, - long long now, int *detect_change); +ASS_Image *ass_render_frame(ASS_Renderer *priv, ASS_Track *track, + long long now, int *detect_change); /* @@ -234,27 +247,27 @@ ass_image_t *ass_render_frame(ass_renderer_t *priv, ass_track_t *track, * \param library handle * \return pointer to empty track */ -ass_track_t *ass_new_track(ass_library_t *); +ASS_Track *ass_new_track(ASS_Library *); /** * \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 *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 *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 *track); /** * \brief Delete a style. @@ -262,7 +275,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 *track, int sid); /** * \brief Delete an event. @@ -270,7 +283,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 *track, int eid); /** * \brief Parse a chunk of subtitle stream data. @@ -278,7 +291,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 *track, char *data, int size); /** * \brief Parse Codec Private section of subtitle stream. @@ -286,7 +299,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 *track, char *data, int size); /** * \brief Parse a chunk of subtitle stream data. In Matroska, @@ -297,7 +310,7 @@ 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, +void ass_process_chunk(ASS_Track *track, char *data, int size, long long timecode, long long duration); /** @@ -307,8 +320,8 @@ void ass_process_chunk(ass_track_t *track, char *data, int size, * \param codepage encoding (iconv format) * \return newly allocated track */ -ass_track_t *ass_read_file(ass_library_t *library, char *fname, - char *codepage); +ASS_Track *ass_read_file(ASS_Library *library, char *fname, + char *codepage); /** * \brief Read subtitles from memory. @@ -318,15 +331,15 @@ ass_track_t *ass_read_file(ass_library_t *library, char *fname, * \param codepage encoding (iconv format) * \return newly allocated track */ -ass_track_t *ass_read_memory(ass_library_t *library, char *buf, - size_t bufsize, char *codepage); +ASS_Track *ass_read_memory(ASS_Library *library, char *buf, + size_t bufsize, char *codepage); /** * \brief Read styles from file into already initialized track. * \param fname file name * \param codepage encoding (iconv format) * \return 0 on success */ -int ass_read_styles(ass_track_t *track, char *fname, char *codepage); +int ass_read_styles(ASS_Track *track, char *fname, char *codepage); /** * \brief Add a memory font. @@ -335,14 +348,14 @@ 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, +void ass_add_font(ASS_Library *library, char *name, char *data, int data_size); /** * \brief Remove all fonts stored in an ass_library object. * \param library library handle */ -void ass_clear_fonts(ass_library_t *library); +void ass_clear_fonts(ASS_Library *library); /** * \brief Calculates timeshift from now to the start of some other subtitle @@ -353,6 +366,6 @@ void ass_clear_fonts(ass_library_t *library); * +2 means "the one after the next", -1 means "previous" * \return timeshift in milliseconds */ -long long ass_step_sub(ass_track_t *track, long long now, int movement); +long long ass_step_sub(ASS_Track *track, long long now, int movement); #endif /* LIBASS_ASS_H */ diff --git a/aegisub/libass/ass_bitmap.c b/aegisub/libass/ass_bitmap.c index 3642be826..1ae2d7c3e 100644 --- a/aegisub/libass/ass_bitmap.c +++ b/aegisub/libass/ass_bitmap.c @@ -28,7 +28,7 @@ #include "ass_utils.h" #include "ass_bitmap.h" -struct ass_synth_priv_s { +struct ass_synth_priv { int tmp_w, tmp_h; unsigned short *tmp; @@ -44,7 +44,7 @@ struct ass_synth_priv_s { 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_SynthPriv *priv, double radius) { double A = log(1.0 / base) / (radius * radius * 2); int mx, i; @@ -101,7 +101,7 @@ static int generate_tables(ass_synth_priv_t *priv, double radius) return 0; } -static void resize_tmp(ass_synth_priv_t *priv, int w, int h) +static void resize_tmp(ASS_SynthPriv *priv, int w, int h) { if (priv->tmp_w >= w && priv->tmp_h >= h) return; @@ -118,14 +118,14 @@ static void resize_tmp(ass_synth_priv_t *priv, int w, int h) priv->tmp = malloc((priv->tmp_w + 1) * priv->tmp_h * sizeof(short)); } -ass_synth_priv_t *ass_synth_init(double radius) +ASS_SynthPriv *ass_synth_init(double radius) { - ass_synth_priv_t *priv = calloc(1, sizeof(ass_synth_priv_t)); + ASS_SynthPriv *priv = calloc(1, sizeof(ASS_SynthPriv)); generate_tables(priv, radius); return priv; } -void ass_synth_done(ass_synth_priv_t *priv) +void ass_synth_done(ASS_SynthPriv *priv) { if (priv->tmp) free(priv->tmp); @@ -136,10 +136,10 @@ void ass_synth_done(ass_synth_priv_t *priv) free(priv); } -static bitmap_t *alloc_bitmap(int w, int h) +static Bitmap *alloc_bitmap(int w, int h) { - bitmap_t *bm; - bm = calloc(1, sizeof(bitmap_t)); + Bitmap *bm; + bm = calloc(1, sizeof(Bitmap)); bm->buffer = malloc(w * h); bm->w = w; bm->h = h; @@ -147,7 +147,7 @@ static bitmap_t *alloc_bitmap(int w, int h) return bm; } -void ass_free_bitmap(bitmap_t *bm) +void ass_free_bitmap(Bitmap *bm) { if (bm) { if (bm->buffer) @@ -156,16 +156,16 @@ void ass_free_bitmap(bitmap_t *bm) } } -static bitmap_t *copy_bitmap(const bitmap_t *src) +static Bitmap *copy_bitmap(const Bitmap *src) { - bitmap_t *dst = alloc_bitmap(src->w, src->h); + Bitmap *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(ass_library_t *library, FT_Glyph glyph) +static int check_glyph_area(ASS_Library *library, FT_Glyph glyph) { FT_BBox bbox; long long dx, dy; @@ -180,12 +180,12 @@ static int check_glyph_area(ass_library_t *library, FT_Glyph glyph) return 0; } -static bitmap_t *glyph_to_bitmap_internal(ass_library_t *library, +static Bitmap *glyph_to_bitmap_internal(ASS_Library *library, FT_Glyph glyph, int bord) { FT_BitmapGlyph bg; FT_Bitmap *bit; - bitmap_t *bm; + Bitmap *bm; int w, h; unsigned char *src; unsigned char *dst; @@ -235,7 +235,7 @@ static bitmap_t *glyph_to_bitmap_internal(ass_library_t *library, * 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 *fix_outline_and_shadow(Bitmap *bm_g, Bitmap *bm_o) { int x, y; const int l = bm_o->left > bm_g->left ? bm_o->left : bm_g->left; @@ -247,7 +247,7 @@ static bitmap_t *fix_outline_and_shadow(bitmap_t *bm_g, bitmap_t *bm_o) 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 *bm_s = copy_bitmap(bm_o); unsigned char *g = bm_g->buffer + (t - bm_g->top) * bm_g->w + (l - bm_g->left); @@ -261,7 +261,7 @@ static bitmap_t *fix_outline_and_shadow(bitmap_t *bm_g, bitmap_t *bm_o) unsigned char c_g, c_o; c_g = g[x]; c_o = o[x]; - o[x] = (c_o > (3 * c_g) / 5) ? c_o - (3 * c_g) / 5 : 0; + 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; @@ -472,10 +472,11 @@ static void be_blur(unsigned char *buf, int w, int h) } } -int glyph_to_bitmap(ass_library_t *library, ass_synth_priv_t *priv_blur, +int glyph_to_bitmap(ASS_Library *library, ASS_SynthPriv *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) + Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s, + int be, double blur_radius, FT_Vector shadow_offset, + int border_style) { blur_radius *= 2; int bbord = be > 0 ? sqrt(2 * be) : 0; @@ -496,7 +497,6 @@ int glyph_to_bitmap(ass_library_t *library, ass_synth_priv_t *priv_blur, if (outline_glyph) { *bm_o = glyph_to_bitmap_internal(library, outline_glyph, bord); if (!*bm_o) { - ass_free_bitmap(*bm_g); return 1; } } @@ -528,7 +528,9 @@ int glyph_to_bitmap(ass_library_t *library, ass_synth_priv_t *priv_blur, priv_blur->g_w); } - if (*bm_o) + if (*bm_o && border_style == 3) + *bm_s = copy_bitmap(*bm_o); + else if (*bm_o) *bm_s = fix_outline_and_shadow(*bm_g, *bm_o); else *bm_s = copy_bitmap(*bm_g); diff --git a/aegisub/libass/ass_bitmap.h b/aegisub/libass/ass_bitmap.h index 2e34a5c6d..338db0119 100644 --- a/aegisub/libass/ass_bitmap.h +++ b/aegisub/libass/ass_bitmap.h @@ -26,16 +26,16 @@ #include "ass.h" -typedef struct ass_synth_priv_s ass_synth_priv_t; +typedef struct ass_synth_priv ASS_SynthPriv; -ass_synth_priv_t *ass_synth_init(double); -void ass_synth_done(ass_synth_priv_t *priv); +ASS_SynthPriv *ass_synth_init(double); +void ass_synth_done(ASS_SynthPriv *priv); -typedef struct bitmap_s { +typedef struct { int left, top; int w, h; // width, height unsigned char *buffer; // w x h buffer -} bitmap_t; +} Bitmap; /** * \brief perform glyph rendering @@ -46,11 +46,12 @@ 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_library_t *library, ass_synth_priv_t *priv_blur, +int glyph_to_bitmap(ASS_Library *library, ASS_SynthPriv *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); + Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s, + int be, double blur_radius, FT_Vector shadow_offset, + int border_style); -void ass_free_bitmap(bitmap_t *bm); +void ass_free_bitmap(Bitmap *bm); #endif /* LIBASS_BITMAP_H */ diff --git a/aegisub/libass/ass_cache.c b/aegisub/libass/ass_cache.c index 524a1c83e..643d9912e 100644 --- a/aegisub/libass/ass_cache.c +++ b/aegisub/libass/ass_cache.c @@ -51,13 +51,13 @@ static void hashmap_item_dtor(void *key, size_t key_size, void *value, free(value); } -hashmap_t *hashmap_init(ass_library_t *library, 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) +Hashmap *hashmap_init(ASS_Library *library, size_t key_size, + size_t value_size, int nbuckets, + HashmapItemDtor item_dtor, + HashmapKeyCompare key_compare, + HashmapHash hash) { - hashmap_t *map = calloc(1, sizeof(hashmap_t)); + Hashmap *map = calloc(1, sizeof(Hashmap)); map->library = library; map->nbuckets = nbuckets; map->key_size = key_size; @@ -69,7 +69,7 @@ hashmap_t *hashmap_init(ass_library_t *library, size_t key_size, return map; } -void hashmap_done(hashmap_t *map) +void hashmap_done(Hashmap *map) { int i; // print stats @@ -81,9 +81,9 @@ void hashmap_done(hashmap_t *map) map->miss_count, map->count); for (i = 0; i < map->nbuckets; ++i) { - hashmap_item_t *item = map->root[i]; + HashmapItem *item = map->root[i]; while (item) { - hashmap_item_t *next = item->next; + HashmapItem *next = item->next; map->item_dtor(item->key, map->key_size, item->value, map->value_size); free(item); @@ -95,17 +95,17 @@ void hashmap_done(hashmap_t *map) } // does nothing if key already exists -void *hashmap_insert(hashmap_t *map, void *key, void *value) +void *hashmap_insert(Hashmap *map, void *key, void *value) { unsigned hash = map->hash(key, map->key_size); - hashmap_item_t **next = map->root + (hash % map->nbuckets); + HashmapItem **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) = malloc(sizeof(HashmapItem)); (*next)->key = malloc(map->key_size); (*next)->value = malloc(map->value_size); memcpy((*next)->key, key, map->key_size); @@ -116,10 +116,10 @@ void *hashmap_insert(hashmap_t *map, void *key, void *value) return (*next)->value; } -void *hashmap_find(hashmap_t *map, void *key) +void *hashmap_find(Hashmap *map, void *key) { unsigned hash = map->hash(key, map->key_size); - hashmap_item_t *item = map->root[hash % map->nbuckets]; + HashmapItem *item = map->root[hash % map->nbuckets]; while (item) { if (map->key_compare(key, item->key, map->key_size)) { map->hit_count++; @@ -136,7 +136,7 @@ void *hashmap_find(hashmap_t *map, void *key) static unsigned font_desc_hash(void *buf, size_t len) { - ass_font_desc_t *desc = buf; + ASS_FontDesc *desc = buf; unsigned hval; hval = fnv_32a_str(desc->family, FNV1_32A_INIT); hval = fnv_32a_buf(&desc->bold, sizeof(desc->bold), hval); @@ -146,8 +146,8 @@ static unsigned font_desc_hash(void *buf, size_t len) static int font_compare(void *key1, void *key2, size_t key_size) { - ass_font_desc_t *a = key1; - ass_font_desc_t *b = key2; + ASS_FontDesc *a = key1; + ASS_FontDesc *b = key2; if (strcmp(a->family, b->family) != 0) return 0; if (a->bold != b->bold) @@ -166,8 +166,8 @@ static void font_hash_dtor(void *key, size_t key_size, void *value, free(key); } -ass_font_t *ass_font_cache_find(hashmap_t *font_cache, - ass_font_desc_t *desc) +ASS_Font *ass_font_cache_find(Hashmap *font_cache, + ASS_FontDesc *desc) { return hashmap_find(font_cache, desc); } @@ -176,22 +176,22 @@ ass_font_t *ass_font_cache_find(hashmap_t *font_cache, * \brief Add a face struct to cache. * \param font font struct */ -void *ass_font_cache_add(hashmap_t *font_cache, ass_font_t *font) +void *ass_font_cache_add(Hashmap *font_cache, ASS_Font *font) { return hashmap_insert(font_cache, &(font->desc), font); } -hashmap_t *ass_font_cache_init(ass_library_t *library) +Hashmap *ass_font_cache_init(ASS_Library *library) { - hashmap_t *font_cache; - font_cache = hashmap_init(library, sizeof(ass_font_desc_t), - sizeof(ass_font_t), + Hashmap *font_cache; + font_cache = hashmap_init(library, sizeof(ASS_FontDesc), + sizeof(ASS_Font), 1000, font_hash_dtor, font_compare, font_desc_hash); return font_cache; } -void ass_font_cache_done(hashmap_t *font_cache) +void ass_font_cache_done(Hashmap *font_cache) { hashmap_done(font_cache); } @@ -209,7 +209,7 @@ void ass_font_cache_done(hashmap_t *font_cache) static void bitmap_hash_dtor(void *key, size_t key_size, void *value, size_t value_size) { - bitmap_hash_val_t *v = value; + BitmapHashValue *v = value; if (v->bm) ass_free_bitmap(v->bm); if (v->bm_o) @@ -220,13 +220,13 @@ static void bitmap_hash_dtor(void *key, size_t key_size, void *value, free(value); } -void *cache_add_bitmap(hashmap_t *bitmap_cache, bitmap_hash_key_t *key, - bitmap_hash_val_t *val) +void *cache_add_bitmap(Hashmap *bitmap_cache, BitmapHashKey *key, + BitmapHashValue *val) { // Note: this is only an approximation if (val->bm_o) bitmap_cache->cache_size += val->bm_o->w * val->bm_o->h * 3; - else + else if (val->bm) bitmap_cache->cache_size += val->bm->w * val->bm->h * 3; return hashmap_insert(bitmap_cache, key, val); @@ -237,32 +237,32 @@ void *cache_add_bitmap(hashmap_t *bitmap_cache, bitmap_hash_key_t *key, * \param key hash key * \return requested hash val or 0 if not found */ -bitmap_hash_val_t *cache_find_bitmap(hashmap_t *bitmap_cache, - bitmap_hash_key_t *key) +BitmapHashValue *cache_find_bitmap(Hashmap *bitmap_cache, + BitmapHashKey *key) { return hashmap_find(bitmap_cache, key); } -hashmap_t *ass_bitmap_cache_init(ass_library_t *library) +Hashmap *ass_bitmap_cache_init(ASS_Library *library) { - hashmap_t *bitmap_cache; + Hashmap *bitmap_cache; bitmap_cache = hashmap_init(library, - sizeof(bitmap_hash_key_t), - sizeof(bitmap_hash_val_t), + sizeof(BitmapHashKey), + sizeof(BitmapHashValue), 0xFFFF + 13, bitmap_hash_dtor, bitmap_compare, bitmap_hash); return bitmap_cache; } -void ass_bitmap_cache_done(hashmap_t *bitmap_cache) +void ass_bitmap_cache_done(Hashmap *bitmap_cache) { hashmap_done(bitmap_cache); } -hashmap_t *ass_bitmap_cache_reset(hashmap_t *bitmap_cache) +Hashmap *ass_bitmap_cache_reset(Hashmap *bitmap_cache) { - ass_library_t *lib = bitmap_cache->library; + ASS_Library *lib = bitmap_cache->library; ass_bitmap_cache_done(bitmap_cache); return ass_bitmap_cache_init(lib); @@ -274,7 +274,7 @@ hashmap_t *ass_bitmap_cache_reset(hashmap_t *bitmap_cache) static void glyph_hash_dtor(void *key, size_t key_size, void *value, size_t value_size) { - glyph_hash_val_t *v = value; + GlyphHashValue *v = value; if (v->glyph) FT_Done_Glyph(v->glyph); if (v->outline_glyph) @@ -283,8 +283,8 @@ static void glyph_hash_dtor(void *key, size_t key_size, void *value, free(value); } -void *cache_add_glyph(hashmap_t *glyph_cache, glyph_hash_key_t *key, - glyph_hash_val_t *val) +void *cache_add_glyph(Hashmap *glyph_cache, GlyphHashKey *key, + GlyphHashValue *val) { return hashmap_insert(glyph_cache, key, val); } @@ -294,30 +294,30 @@ void *cache_add_glyph(hashmap_t *glyph_cache, glyph_hash_key_t *key, * \param key hash key * \return requested hash val or 0 if not found */ -glyph_hash_val_t *cache_find_glyph(hashmap_t *glyph_cache, - glyph_hash_key_t *key) +GlyphHashValue *cache_find_glyph(Hashmap *glyph_cache, + GlyphHashKey *key) { return hashmap_find(glyph_cache, key); } -hashmap_t *ass_glyph_cache_init(ass_library_t *library) +Hashmap *ass_glyph_cache_init(ASS_Library *library) { - hashmap_t *glyph_cache; - glyph_cache = hashmap_init(library, sizeof(glyph_hash_key_t), - sizeof(glyph_hash_val_t), + Hashmap *glyph_cache; + glyph_cache = hashmap_init(library, sizeof(GlyphHashKey), + sizeof(GlyphHashValue), 0xFFFF + 13, glyph_hash_dtor, glyph_compare, glyph_hash); return glyph_cache; } -void ass_glyph_cache_done(hashmap_t *glyph_cache) +void ass_glyph_cache_done(Hashmap *glyph_cache) { hashmap_done(glyph_cache); } -hashmap_t *ass_glyph_cache_reset(hashmap_t *glyph_cache) +Hashmap *ass_glyph_cache_reset(Hashmap *glyph_cache) { - ass_library_t *lib = glyph_cache->library; + ASS_Library *lib = glyph_cache->library; ass_glyph_cache_done(glyph_cache); return ass_glyph_cache_init(lib); @@ -330,16 +330,16 @@ hashmap_t *ass_glyph_cache_reset(hashmap_t *glyph_cache) static void composite_hash_dtor(void *key, size_t key_size, void *value, size_t value_size) { - composite_hash_val_t *v = value; + CompositeHashValue *v = value; free(v->a); free(v->b); free(key); free(value); } -void *cache_add_composite(hashmap_t *composite_cache, - composite_hash_key_t *key, - composite_hash_val_t *val) +void *cache_add_composite(Hashmap *composite_cache, + CompositeHashKey *key, + CompositeHashValue *val) { return hashmap_insert(composite_cache, key, val); } @@ -349,31 +349,31 @@ void *cache_add_composite(hashmap_t *composite_cache, * \param key hash key * \return requested hash val or 0 if not found */ -composite_hash_val_t *cache_find_composite(hashmap_t *composite_cache, - composite_hash_key_t *key) +CompositeHashValue *cache_find_composite(Hashmap *composite_cache, + CompositeHashKey *key) { return hashmap_find(composite_cache, key); } -hashmap_t *ass_composite_cache_init(ass_library_t *library) +Hashmap *ass_composite_cache_init(ASS_Library *library) { - hashmap_t *composite_cache; - composite_cache = hashmap_init(library, sizeof(composite_hash_key_t), - sizeof(composite_hash_val_t), + Hashmap *composite_cache; + composite_cache = hashmap_init(library, sizeof(CompositeHashKey), + sizeof(CompositeHashValue), 0xFFFF + 13, composite_hash_dtor, composite_compare, composite_hash); return composite_cache; } -void ass_composite_cache_done(hashmap_t *composite_cache) +void ass_composite_cache_done(Hashmap *composite_cache) { hashmap_done(composite_cache); } -hashmap_t *ass_composite_cache_reset(hashmap_t *composite_cache) +Hashmap *ass_composite_cache_reset(Hashmap *composite_cache) { - ass_library_t *lib = composite_cache->library; + ASS_Library *lib = composite_cache->library; ass_composite_cache_done(composite_cache); return ass_composite_cache_init(lib); diff --git a/aegisub/libass/ass_cache.h b/aegisub/libass/ass_cache.h index e31d8cf68..5c9749f87 100644 --- a/aegisub/libass/ass_cache.h +++ b/aegisub/libass/ass_cache.h @@ -25,95 +25,95 @@ #include "ass_font.h" #include "ass_bitmap.h" -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 void (*HashmapItemDtor) (void *key, size_t key_size, + void *value, size_t value_size); +typedef int (*HashmapKeyCompare) (void *key1, void *key2, + size_t key_size); +typedef unsigned (*HashmapHash) (void *key, size_t key_size); -typedef struct hashmap_item_s { +typedef struct hashmap_item { void *key; void *value; - struct hashmap_item_s *next; -} hashmap_item_t; -typedef hashmap_item_t *hashmap_item_p; + struct hashmap_item *next; +} HashmapItem; +typedef HashmapItem *hashmap_item_p; -typedef struct hashmap_s { +typedef struct { 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; + HashmapItemDtor item_dtor; // a destructor for hashmap key/value pairs + HashmapKeyCompare key_compare; + HashmapHash hash; size_t cache_size; // stats int hit_count; int miss_count; int count; - ass_library_t *library; -} hashmap_t; + ASS_Library *library; +} Hashmap; -hashmap_t *hashmap_init(ass_library_t *library, 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 *hashmap_init(ASS_Library *library, size_t key_size, + size_t value_size, int nbuckets, + HashmapItemDtor item_dtor, + HashmapKeyCompare key_compare, + HashmapHash hash); +void hashmap_done(Hashmap *map); +void *hashmap_insert(Hashmap *map, void *key, void *value); +void *hashmap_find(Hashmap *map, void *key); -hashmap_t *ass_font_cache_init(ass_library_t *library); -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 *); +Hashmap *ass_font_cache_init(ASS_Library *library); +ASS_Font *ass_font_cache_find(Hashmap *, ASS_FontDesc *desc); +void *ass_font_cache_add(Hashmap *, ASS_Font *font); +void ass_font_cache_done(Hashmap *); // Create definitions for bitmap_hash_key and glyph_hash_key #define CREATE_STRUCT_DEFINITIONS #include "ass_cache_template.h" -typedef struct bitmap_hash_val_s { - bitmap_t *bm; // the actual bitmaps - bitmap_t *bm_o; - bitmap_t *bm_s; -} bitmap_hash_val_t; +typedef struct { + Bitmap *bm; // the actual bitmaps + Bitmap *bm_o; + Bitmap *bm_s; +} BitmapHashValue; -hashmap_t *ass_bitmap_cache_init(ass_library_t *library); -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); +Hashmap *ass_bitmap_cache_init(ASS_Library *library); +void *cache_add_bitmap(Hashmap *, BitmapHashKey *key, + BitmapHashValue *val); +BitmapHashValue *cache_find_bitmap(Hashmap *bitmap_cache, + BitmapHashKey *key); +Hashmap *ass_bitmap_cache_reset(Hashmap *bitmap_cache); +void ass_bitmap_cache_done(Hashmap *bitmap_cache); -typedef struct composite_hash_val_s { +typedef struct { unsigned char *a; unsigned char *b; -} composite_hash_val_t; +} CompositeHashValue; -hashmap_t *ass_composite_cache_init(ass_library_t *library); -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); +Hashmap *ass_composite_cache_init(ASS_Library *library); +void *cache_add_composite(Hashmap *, CompositeHashKey *key, + CompositeHashValue *val); +CompositeHashValue *cache_find_composite(Hashmap *composite_cache, + CompositeHashKey *key); +Hashmap *ass_composite_cache_reset(Hashmap *composite_cache); +void ass_composite_cache_done(Hashmap *composite_cache); -typedef struct glyph_hash_val_s { +typedef struct { 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; +} GlyphHashValue; -hashmap_t *ass_glyph_cache_init(ass_library_t *library); -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); +Hashmap *ass_glyph_cache_init(ASS_Library *library); +void *cache_add_glyph(Hashmap *, GlyphHashKey *key, + GlyphHashValue *val); +GlyphHashValue *cache_find_glyph(Hashmap *glyph_cache, + GlyphHashKey *key); +Hashmap *ass_glyph_cache_reset(Hashmap *glyph_cache); +void ass_glyph_cache_done(Hashmap *glyph_cache); #endif /* LIBASS_CACHE_H */ diff --git a/aegisub/libass/ass_cache_template.h b/aegisub/libass/ass_cache_template.h index eae980747..f335c6b27 100644 --- a/aegisub/libass/ass_cache_template.h +++ b/aegisub/libass/ass_cache_template.h @@ -7,7 +7,7 @@ #define FTVECTOR(member) \ FT_Vector member; #define BITMAPHASHKEY(member) \ - bitmap_hash_key_t member; + BitmapHashKey member; #define END(typedefnamename) \ } typedefnamename; @@ -54,9 +54,9 @@ // describes a bitmap; bitmaps with equivalents structs are considered identical -START(bitmap, bipmap_hash_key_s) +START(bitmap, bitmap_hash_key) GENERIC(char, bitmap) // bool : true = bitmap, false = outline - GENERIC(ass_font_t *, font) + GENERIC(ASS_Font *, font) GENERIC(double, size) // font size GENERIC(uint32_t, ch) // character code FTVECTOR(outline) // border width, 16.16 fixed point value @@ -79,25 +79,27 @@ START(bitmap, bipmap_hash_key_s) FTVECTOR(advance) // subpixel shift vector FTVECTOR(shadow_offset) // shadow subpixel shift GENERIC(unsigned, drawing_hash) // hashcode of a drawing -END(bitmap_hash_key_t) + GENERIC(unsigned, flags) // glyph decoration + GENERIC(unsigned, border_style) +END(BitmapHashKey) // describes an outline glyph -START(glyph, glyph_hash_key_s) - GENERIC(ass_font_t *, font) +START(glyph, glyph_hash_key) + GENERIC(ASS_Font *, font) GENERIC(double, size) // font size GENERIC(uint32_t, ch) // character code GENERIC(int, bold) GENERIC(int, italic) GENERIC(unsigned, scale_x) // 16.16 GENERIC(unsigned, scale_y) // 16.16 - FTVECTOR(advance) // subpixel shift vector 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) + GENERIC(unsigned, border_style) +END(GlyphHashKey) // Cache for composited bitmaps -START(composite, composite_hash_key_s) +START(composite, composite_hash_key) GENERIC(int, aw) GENERIC(int, ah) GENERIC(int, bw) @@ -106,9 +108,11 @@ START(composite, composite_hash_key_s) GENERIC(int, ay) GENERIC(int, bx) GENERIC(int, by) - BITMAPHASHKEY(a) - BITMAPHASHKEY(b) -END(composite_hash_key_t) + GENERIC(int, as) + GENERIC(int, bs) + GENERIC(unsigned char *, a) + GENERIC(unsigned char *, b) +END(CompositeHashKey) #undef START diff --git a/aegisub/libass/ass_drawing.c b/aegisub/libass/ass_drawing.c index 0b270c1a1..8c5f062bc 100644 --- a/aegisub/libass/ass_drawing.c +++ b/aegisub/libass/ass_drawing.c @@ -33,8 +33,8 @@ /* * \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) +static void drawing_make_glyph(ASS_Drawing *drawing, void *fontconfig_priv, + ASS_Font *font, ASS_Hinting hint) { FT_OutlineGlyph glyph; @@ -55,7 +55,7 @@ static void drawing_make_glyph(ass_drawing_t *drawing, void *fontconfig_priv, /* * \brief Add a single point to a contour. */ -static inline void drawing_add_point(ass_drawing_t *drawing, +static inline void drawing_add_point(ASS_Drawing *drawing, FT_Vector *point) { FT_Outline *ol = &drawing->glyph->outline; @@ -76,7 +76,7 @@ static inline void drawing_add_point(ass_drawing_t *drawing, /* * \brief Close a contour and check glyph size overflow. */ -static inline void drawing_close_shape(ass_drawing_t *drawing) +static inline void drawing_close_shape(ASS_Drawing *drawing) { FT_Outline *ol = &drawing->glyph->outline; @@ -86,14 +86,16 @@ static inline void drawing_close_shape(ass_drawing_t *drawing) drawing->max_contours); } - ol->contours[ol->n_contours] = ol->n_points - 1; - ol->n_contours++; + if (ol->n_points) { + 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) +static void drawing_prepare(ASS_Drawing *drawing) { // Scaling parameters drawing->point_scale_x = drawing->scale_x * @@ -106,7 +108,7 @@ static void drawing_prepare(ass_drawing_t *drawing) * \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 raw_mode) +static void drawing_finish(ASS_Drawing *drawing, int raw_mode) { int i, offset; FT_BBox bbox; @@ -150,7 +152,7 @@ static void drawing_finish(ass_drawing_t *drawing, int raw_mode) /* * \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) +static int token_check_values(ASS_DrawingToken *token, int i, int type) { int j; for (j = 0; j < i; j++) { @@ -162,16 +164,16 @@ static int token_check_values(ass_drawing_token_t *token, int i, int type) } /* - * \brief Tokenize a drawing string into a list of ass_drawing_token_t + * \brief Tokenize a drawing string into a list of ASS_DrawingToken * This also expands points for closing b-splines */ -static ass_drawing_token_t *drawing_tokenize(char *str) +static ASS_DrawingToken *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; + ASS_DrawingToken *root = NULL, *tail = NULL, *spline_start = NULL; while (*p) { if (*p == 'c' && spline_start) { @@ -179,7 +181,7 @@ static ass_drawing_token_t *drawing_tokenize(char *str) // 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 = calloc(1, sizeof(ASS_DrawingToken)); tail->next->prev = tail; tail = tail->next; tail->type = TOKEN_B_SPLINE; @@ -214,11 +216,11 @@ static ass_drawing_token_t *drawing_tokenize(char *str) if (type != -1 && is_set == 2) { if (root) { - tail->next = calloc(1, sizeof(ass_drawing_token_t)); + tail->next = calloc(1, sizeof(ASS_DrawingToken)); tail->next->prev = tail; tail = tail->next; } else - root = tail = calloc(1, sizeof(ass_drawing_token_t)); + root = tail = calloc(1, sizeof(ASS_DrawingToken)); tail->type = type; tail->point = point; is_set = 0; @@ -230,7 +232,7 @@ static ass_drawing_token_t *drawing_tokenize(char *str) #if 0 // Check tokens - ass_drawing_token_t *t = root; + ASS_DrawingToken *t = root; while(t) { printf("token %d point (%d, %d)\n", t->type, t->point.x, t->point.y); t = t->next; @@ -243,10 +245,10 @@ static ass_drawing_token_t *drawing_tokenize(char *str) /* * \brief Free a list of tokens */ -static void drawing_free_tokens(ass_drawing_token_t *token) +static void drawing_free_tokens(ASS_DrawingToken *token) { while (token) { - ass_drawing_token_t *at = token; + ASS_DrawingToken *at = token; token = token->next; free(at); } @@ -256,7 +258,7 @@ static void drawing_free_tokens(ass_drawing_token_t *token) * \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) +static inline void translate_point(ASS_Drawing *drawing, FT_Vector *point) { point->x = drawing->point_scale_x * point->x; point->y = drawing->point_scale_y * -point->y; @@ -267,8 +269,8 @@ static inline void translate_point(ass_drawing_t *drawing, FT_Vector *point) * 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, +static void drawing_evaluate_curve(ASS_Drawing *drawing, + ASS_DrawingToken *token, char spline, int started) { double cx3, cx2, cx1, cx0, cy3, cy2, cy1, cy0; @@ -358,10 +360,10 @@ static void drawing_evaluate_curve(ass_drawing_t *drawing, /* * \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 *ass_drawing_new(void *fontconfig_priv, ASS_Font *font, + ASS_Hinting hint, FT_Library lib) { - ass_drawing_t* drawing; + ASS_Drawing* drawing; drawing = calloc(1, sizeof(*drawing)); drawing->text = calloc(1, DRAWING_INITIAL_SIZE); @@ -382,7 +384,7 @@ ass_drawing_t *ass_drawing_new(void *fontconfig_priv, ass_font_t *font, /* * \brief Free a drawing */ -void ass_drawing_free(ass_drawing_t* drawing) +void ass_drawing_free(ASS_Drawing* drawing) { FT_Done_Glyph((FT_Glyph) drawing->glyph); free(drawing->text); @@ -392,7 +394,7 @@ void ass_drawing_free(ass_drawing_t* drawing) /* * \brief Add one ASCII character to the drawing text buffer */ -void ass_drawing_add_char(ass_drawing_t* drawing, char symbol) +void ass_drawing_add_char(ASS_Drawing* drawing, char symbol) { drawing->text[drawing->i++] = symbol; drawing->text[drawing->i] = 0; @@ -407,7 +409,7 @@ void ass_drawing_add_char(ass_drawing_t* drawing, char symbol) * \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) +void ass_drawing_hash(ASS_Drawing* drawing) { drawing->hash = fnv_32a_str(drawing->text, FNV1_32A_INIT); } @@ -415,10 +417,10 @@ void ass_drawing_hash(ass_drawing_t* drawing) /* * \brief Convert token list to outline. Calls the line and curve evaluators. */ -FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing, int raw_mode) +FT_OutlineGlyph *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode) { int started = 0; - ass_drawing_token_t *token; + ASS_DrawingToken *token; FT_Vector pen = {0, 0}; drawing->tokens = drawing_tokenize(drawing->text); diff --git a/aegisub/libass/ass_drawing.h b/aegisub/libass/ass_drawing.h index 25cc4a721..913588e74 100644 --- a/aegisub/libass/ass_drawing.h +++ b/aegisub/libass/ass_drawing.h @@ -26,7 +26,7 @@ #define DRAWING_INITIAL_SIZE 256 -enum ass_token_type { +typedef enum { TOKEN_MOVE, TOKEN_MOVE_NC, TOKEN_LINE, @@ -35,16 +35,16 @@ enum ass_token_type { TOKEN_B_SPLINE, TOKEN_EXTEND_SPLINE, TOKEN_CLOSE -}; +} ASS_TokenType; -typedef struct ass_drawing_token_s { - enum ass_token_type type; +typedef struct ass_drawing_token { + ASS_TokenType type; FT_Vector point; - struct ass_drawing_token_s *next; - struct ass_drawing_token_s *prev; -} ass_drawing_token_t; + struct ass_drawing_token *next; + struct ass_drawing_token *prev; +} ASS_DrawingToken; -typedef struct ass_drawing_s { +typedef struct { char *text; // drawing string int i; // text index int scale; // scale (1-64) for subpixel accuracy @@ -58,20 +58,20 @@ typedef struct ass_drawing_s { // private FT_Library ftlibrary; // FT library instance, needed for font ops - ass_library_t *library; + ASS_Library *library; int size; // current buffer size - ass_drawing_token_t *tokens; // tokenized drawing + ASS_DrawingToken *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; -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, int raw_mode); +ASS_Drawing *ass_drawing_new(void *fontconfig_priv, ASS_Font *font, + ASS_Hinting hint, FT_Library lib); +void ass_drawing_free(ASS_Drawing* drawing); +void ass_drawing_add_char(ASS_Drawing* drawing, char symbol); +void ass_drawing_hash(ASS_Drawing* drawing); +FT_OutlineGlyph *ass_drawing_parse(ASS_Drawing *drawing, int raw_mode); #endif /* LIBASS_DRAWING_H */ diff --git a/aegisub/libass/ass_font.c b/aegisub/libass/ass_font.c index 35d34cf49..2e8548c5b 100644 --- a/aegisub/libass/ass_font.c +++ b/aegisub/libass/ass_font.c @@ -26,6 +26,7 @@ #include FT_SYNTHESIS_H #include FT_GLYPH_H #include FT_TRUETYPE_TABLES_H +#include FT_OUTLINE_H #include "ass.h" #include "ass_library.h" @@ -39,15 +40,15 @@ * Select Microfost Unicode CharMap, if the font has one. * Otherwise, let FreeType decide. */ -static void charmap_magic(ass_library_t *library, FT_Face face) +static void charmap_magic(ASS_Library *library, 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 */ + if (pid == 3 /*microsoft */ + && (eid == 1 /*unicode bmp */ || eid == 10 /*full unicode */ )) { FT_Set_Charmap(face, cmap); return; @@ -66,7 +67,7 @@ static void charmap_magic(ass_library_t *library, FT_Face face) } } -static void update_transform(ass_font_t *font) +static void update_transform(ASS_Font *font) { int i; FT_Matrix m; @@ -80,7 +81,7 @@ static void update_transform(ass_font_t *font) /** * \brief find a memory font by name */ -static int find_font(ass_library_t *library, char *name) +static int find_font(ASS_Library *library, char *name) { int i; for (i = 0; i < library->num_fontdata; ++i) @@ -111,10 +112,10 @@ static void buggy_font_workaround(FT_Face face) } /** - * \brief Select a face with the given charcode and add it to ass_font_t + * \brief Select a face with the given charcode and add it to ASS_Font * \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 *font, uint32_t ch) { char *path; int index; @@ -166,17 +167,17 @@ static int add_face(void *fc_priv, ass_font_t *font, uint32_t ch) } /** - * \brief Create a new ass_font_t according to "desc" argument + * \brief Create a new ASS_Font according to "desc" argument */ -ass_font_t *ass_font_new(void *font_cache, ass_library_t *library, - FT_Library ftlibrary, void *fc_priv, - ass_font_desc_t *desc) +ASS_Font *ass_font_new(void *font_cache, ASS_Library *library, + FT_Library ftlibrary, void *fc_priv, + ASS_FontDesc *desc) { int error; - ass_font_t *fontp; - ass_font_t font; + ASS_Font *fontp; + ASS_Font font; - fontp = ass_font_cache_find((hashmap_t *) font_cache, desc); + fontp = ass_font_cache_find((Hashmap *) font_cache, desc); if (fontp) return fontp; @@ -197,19 +198,21 @@ ass_font_t *ass_font_new(void *font_cache, ass_library_t *library, free(font.desc.family); return 0; } else - return ass_font_cache_add((hashmap_t *) font_cache, &font); + return ass_font_cache_add((Hashmap *) font_cache, &font); } /** * \brief Set font transformation matrix and shift vector **/ -void ass_font_set_transform(ass_font_t *font, double scale_x, +void ass_font_set_transform(ASS_Font *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; + if (v) { + font->v.x = v->x; + font->v.y = v->y; + } update_transform(font); } @@ -246,7 +249,7 @@ static void face_set_size(FT_Face face, double size) /** * \brief Set font size **/ -void ass_font_set_size(ass_font_t *font, double size) +void ass_font_set_size(ASS_Font *font, double size) { int i; if (font->size != size) { @@ -261,16 +264,22 @@ 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, +void ass_font_get_asc_desc(ASS_Font *font, uint32_t ch, int *asc, int *desc) { int i; for (i = 0; i < font->n_faces; ++i) { FT_Face face = font->faces[i]; + TT_OS2 *os2 = FT_Get_Sfnt_Table(face, ft_sfnt_os2); if (FT_Get_Char_Index(face, ch)) { int y_scale = face->size->metrics.y_scale; - *asc = FT_MulFix(face->ascender, y_scale); - *desc = FT_MulFix(-face->descender, y_scale); + if (os2) { + *asc = FT_MulFix(os2->usWinAscent, y_scale); + *desc = FT_MulFix(os2->usWinDescent, y_scale); + } else { + *asc = FT_MulFix(face->ascender, y_scale); + *desc = FT_MulFix(-face->descender, y_scale); + } return; } } @@ -285,13 +294,16 @@ void ass_font_get_asc_desc(ass_font_t *font, uint32_t ch, int *asc, * being accurate. * */ -static int ass_strike_outline_glyph(FT_Face face, ass_font_t *font, +static int ass_strike_outline_glyph(FT_Face face, ASS_Font *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; + int bear, advance, y_scale, i, dir; + + if (!under && !through) + return 0; // Grow outline i = (under ? 4 : 0) + (through ? 4 : 0); @@ -309,15 +321,18 @@ static int ass_strike_outline_glyph(FT_Face face, ass_font_t *font, advance = d16_to_d6(glyph->advance.x) + 32; y_scale = face->size->metrics.y_scale; + // Reverse drawing direction for non-truetype fonts + dir = FT_Outline_Get_Orientation(ol); + // Add points to the outline - if (under) { + if (under && ps) { 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; + return 1; FT_Vector points[4] = { {.x = bear, .y = pos + size}, @@ -326,20 +341,28 @@ static int ass_strike_outline_glyph(FT_Face face, ass_font_t *font, {.x = bear, .y = pos - size}, }; - for (i = 0; i < 4; i++) { - ol->points[ol->n_points] = points[i]; - ol->tags[ol->n_points++] = 1; + if (dir == FT_ORIENTATION_TRUETYPE) { + for (i = 0; i < 4; i++) { + ol->points[ol->n_points] = points[i]; + ol->tags[ol->n_points++] = 1; + } + } else { + for (i = 3; i >= 0; 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) { + if (through && os2) { 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; + return 1; FT_Vector points[4] = { {.x = bear, .y = pos + size}, @@ -348,23 +371,46 @@ static int ass_strike_outline_glyph(FT_Face face, ass_font_t *font, {.x = bear, .y = pos - size}, }; - for (i = 0; i < 4; i++) { - ol->points[ol->n_points] = points[i]; - ol->tags[ol->n_points++] = 1; + if (dir == FT_ORIENTATION_TRUETYPE) { + for (i = 0; i < 4; i++) { + ol->points[ol->n_points] = points[i]; + ol->tags[ol->n_points++] = 1; + } + } else { + for (i = 3; i >= 0; 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; + return 0; +} + +/** + * Slightly embold a glyph without touching its metrics + */ +static void ass_glyph_embolden(FT_GlyphSlot slot) +{ + int str; + + if (slot->format != FT_GLYPH_FORMAT_OUTLINE) + return; + + str = FT_MulFix(slot->face->units_per_EM, + slot->face->size->metrics.y_scale) / 64; + + FT_Outline_Embolden(&slot->outline, str); } /** * \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, int deco) +FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font, + uint32_t ch, ASS_Hinting hinting, int deco) { int error; int index = 0; @@ -435,6 +481,11 @@ FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ass_font_t *font, (font->desc.italic > 55)) { FT_GlyphSlot_Oblique(face->glyph); } + + if (!(face->style_flags & FT_STYLE_FLAG_BOLD) && + (font->desc.bold > 80)) { + ass_glyph_embolden(face->glyph); + } #endif error = FT_Get_Glyph(face->glyph, &glyph); if (error) { @@ -452,7 +503,7 @@ FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ass_font_t *font, /** * \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 *font, uint32_t c1, uint32_t c2) { FT_Vector v = { 0, 0 }; int i; @@ -473,9 +524,9 @@ FT_Vector ass_font_get_kerning(ass_font_t *font, uint32_t c1, uint32_t c2) } /** - * \brief Deallocate ass_font_t + * \brief Deallocate ASS_Font **/ -void ass_font_free(ass_font_t *font) +void ass_font_free(ASS_Font *font) { int i; for (i = 0; i < font->n_faces; ++i) diff --git a/aegisub/libass/ass_font.h b/aegisub/libass/ass_font.h index 8fe4c1e65..ca0c213a0 100644 --- a/aegisub/libass/ass_font.h +++ b/aegisub/libass/ass_font.h @@ -31,36 +31,36 @@ #define DECO_UNDERLINE 1 #define DECO_STRIKETHROUGH 2 -typedef struct ass_font_desc_s { +typedef struct { char *family; unsigned bold; unsigned italic; int treat_family_as_pattern; -} ass_font_desc_t; +} ASS_FontDesc; -typedef struct ass_font_s { - ass_font_desc_t desc; - ass_library_t *library; +typedef struct { + ASS_FontDesc desc; + ASS_Library *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; // 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, +ASS_Font *ass_font_new(void *font_cache, ASS_Library *library, + FT_Library ftlibrary, void *fc_priv, + ASS_FontDesc *desc); +void ass_font_set_transform(ASS_Font *font, double scale_x, + double scale_y, FT_Vector *v); +void ass_font_set_size(ASS_Font *font, double size); +void ass_font_get_asc_desc(ASS_Font *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); +FT_Glyph ass_font_get_glyph(void *fontconfig_priv, ASS_Font *font, + uint32_t ch, ASS_Hinting hinting, int flags); +FT_Vector ass_font_get_kerning(ASS_Font *font, uint32_t c1, uint32_t c2); +void ass_font_free(ASS_Font *font); #endif /* LIBASS_FONT_H */ diff --git a/aegisub/libass/ass_fontconfig.c b/aegisub/libass/ass_fontconfig.c index 4eee1975f..006be9767 100644 --- a/aegisub/libass/ass_fontconfig.c +++ b/aegisub/libass/ass_fontconfig.c @@ -40,7 +40,7 @@ #include #endif -struct fc_instance_s { +struct fc_instance { #ifdef CONFIG_FONTCONFIG FcConfig *config; #endif @@ -71,7 +71,7 @@ struct fc_instance_s { * \param code: the character that should be present in the font, can be 0 * \return font file path */ -static char *_select_font(ass_library_t *library, fc_instance_t *priv, +static char *_select_font(ASS_Library *library, FCInstance *priv, const char *family, int treat_family_as_pattern, unsigned bold, unsigned italic, int *index, uint32_t code) @@ -242,7 +242,7 @@ static char *_select_font(ass_library_t *library, fc_instance_t *priv, * \param code: the character that should be present in the font, can be 0 * \return font file path */ -char *fontconfig_select(ass_library_t *library, fc_instance_t *priv, +char *fontconfig_select(ASS_Library *library, FCInstance *priv, const char *family, int treat_family_as_pattern, unsigned bold, unsigned italic, int *index, uint32_t code) @@ -331,7 +331,7 @@ static char *validate_fname(char *name) * 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, +static void process_fontdata(FCInstance *priv, ASS_Library *library, FT_Library ftlibrary, int idx) { int rc; @@ -432,13 +432,13 @@ static void process_fontdata(fc_instance_t *priv, ass_library_t *library, * \param update whether the fontconfig cache should be built/updated * \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, const char *config, - int update) +FCInstance *fontconfig_init(ASS_Library *library, + FT_Library ftlibrary, const char *family, + const char *path, int fc, const char *config, + int update) { int rc; - fc_instance_t *priv = calloc(1, sizeof(fc_instance_t)); + FCInstance *priv = calloc(1, sizeof(FCInstance)); const char *dir = library->fonts_dir; int i; @@ -448,17 +448,22 @@ fc_instance_t *fontconfig_init(ass_library_t *library, goto exit; } - if (!config) - config = (char *) FcConfigFilename(NULL); - priv->config = FcConfigCreate(); rc = FcConfigParseAndLoad(priv->config, (unsigned char *) config, FcTrue); + if (!rc) { + ass_msg(library, MSGL_WARN, "No usable fontconfig configuration " + "file found, using fallback."); + FcConfigDestroy(priv->config); + priv->config = FcInitLoadConfig(); + rc++; + } if (rc && update) { FcConfigBuildFonts(priv->config); } if (!rc || !priv->config) { - ass_msg(library, MSGL_FATAL, "%s failed", "FcInitLoadConfigAndFonts"); + ass_msg(library, MSGL_FATAL, + "No valid fontconfig configuration found!"); FcConfigDestroy(priv->config); goto exit; } @@ -516,48 +521,53 @@ exit: return priv; } -int fontconfig_update(fc_instance_t *priv) +int fontconfig_update(FCInstance *priv) { return FcConfigBuildFonts(priv->config); } #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(ASS_Library *library, FCInstance *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; } -fc_instance_t *fontconfig_init(ass_library_t *library, - FT_Library ftlibrary, const char *family, - const char *path, int fc, const char *config, - int update) +FCInstance *fontconfig_init(ASS_Library *library, + FT_Library ftlibrary, const char *family, + const char *path, int fc, const char *config, + int update) { - fc_instance_t *priv; + FCInstance *priv; ass_msg(library, MSGL_WARN, "Fontconfig disabled, only default font will be used."); - priv = calloc(1, sizeof(fc_instance_t)); + priv = calloc(1, sizeof(FCInstance)); priv->path_default = strdup(path); priv->index_default = 0; return priv; } -int fontconfig_update(fc_instance_t *priv) +int fontconfig_update(FCInstance *priv) { // Do nothing + return 1; } #endif -void fontconfig_done(fc_instance_t *priv) +void fontconfig_done(FCInstance *priv) { - // don't call FcFini() here, library can still be used by some code +#ifdef CONFIG_FONTCONFIG + if (priv && priv->config) + FcConfigDestroy(priv->config); +#endif if (priv && priv->path_default) free(priv->path_default); if (priv && priv->family_default) diff --git a/aegisub/libass/ass_fontconfig.h b/aegisub/libass/ass_fontconfig.h index 24c164d9d..ad5b9f0e4 100644 --- a/aegisub/libass/ass_fontconfig.h +++ b/aegisub/libass/ass_fontconfig.h @@ -31,17 +31,17 @@ #include #endif -typedef struct fc_instance_s fc_instance_t; +typedef struct fc_instance FCInstance; -fc_instance_t *fontconfig_init(ass_library_t *library, - FT_Library ftlibrary, const char *family, - const char *path, int fc, const char *config, - int update); -char *fontconfig_select(ass_library_t *library, fc_instance_t *priv, +FCInstance *fontconfig_init(ASS_Library *library, + FT_Library ftlibrary, const char *family, + const char *path, int fc, const char *config, + int update); +char *fontconfig_select(ASS_Library *library, FCInstance *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); -int fontconfig_update(fc_instance_t *priv); +void fontconfig_done(FCInstance *priv); +int fontconfig_update(FCInstance *priv); #endif /* LIBASS_FONTCONFIG_H */ diff --git a/aegisub/libass/ass_library.c b/aegisub/libass/ass_library.c index f464af7aa..53b91af16 100644 --- a/aegisub/libass/ass_library.c +++ b/aegisub/libass/ass_library.c @@ -28,24 +28,24 @@ #include "ass_library.h" #include "ass_utils.h" -static void ass_msg_handler(int level, char *fmt, va_list *va, void *data) +static void ass_msg_handler(int level, const char *fmt, va_list va, void *data) { if (level > MSGL_INFO) return; fprintf(stderr, "[ass] "); - vfprintf(stderr, fmt, *va); + vfprintf(stderr, fmt, va); fprintf(stderr, "\n"); } -ass_library_t *ass_library_init(void) +ASS_Library *ass_library_init(void) { - ass_library_t* lib = calloc(1, sizeof(ass_library_t)); + ASS_Library* lib = calloc(1, sizeof(*lib)); lib->msg_callback = ass_msg_handler; return lib; } -void ass_library_done(ass_library_t *priv) +void ass_library_done(ASS_Library *priv) { if (priv) { ass_set_fonts_dir(priv, NULL); @@ -55,7 +55,7 @@ void ass_library_done(ass_library_t *priv) } } -void ass_set_fonts_dir(ass_library_t *priv, const char *fonts_dir) +void ass_set_fonts_dir(ASS_Library *priv, const char *fonts_dir) { if (priv->fonts_dir) free(priv->fonts_dir); @@ -63,12 +63,12 @@ void ass_set_fonts_dir(ass_library_t *priv, const char *fonts_dir) 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 *priv, int extract) { priv->extract_fonts = !!extract; } -void ass_set_style_overrides(ass_library_t *priv, char **list) +void ass_set_style_overrides(ASS_Library *priv, char **list) { char **p; char **q; @@ -98,7 +98,7 @@ static void grow_array(void **array, int nelem, size_t elsize) *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 *priv, char *name, char *data, int size) { int idx = priv->num_fontdata; if (!name || !data || !size) @@ -116,7 +116,7 @@ void ass_add_font(ass_library_t *priv, char *name, char *data, int size) priv->num_fontdata++; } -void ass_clear_fonts(ass_library_t *priv) +void ass_clear_fonts(ASS_Library *priv) { int i; for (i = 0; i < priv->num_fontdata; ++i) { @@ -136,8 +136,8 @@ void ass_clear_fonts(ass_library_t *priv) * \param msg_cb the callback function * \param data additional data that will be passed to the callback */ -void ass_set_message_cb(ass_library_t *priv, - void (*msg_cb)(int, char *, va_list *, void *), +void ass_set_message_cb(ASS_Library *priv, + void (*msg_cb)(int, const char *, va_list, void *), void *data) { if (msg_cb) { @@ -145,4 +145,3 @@ void ass_set_message_cb(ass_library_t *priv, priv->msg_callback_data = data; } } - diff --git a/aegisub/libass/ass_library.h b/aegisub/libass/ass_library.h index 85b0842db..e0db5c951 100644 --- a/aegisub/libass/ass_library.h +++ b/aegisub/libass/ass_library.h @@ -23,20 +23,20 @@ #include -typedef struct ass_fontdata_s { +typedef struct { char *name; char *data; int size; -} ass_fontdata_t; +} ASS_Fontdata; -struct ass_library_s { +struct ass_library { char *fonts_dir; int extract_fonts; char **style_overrides; - ass_fontdata_t *fontdata; + ASS_Fontdata *fontdata; int num_fontdata; - void (*msg_callback)(int, char *, va_list *, void *); + void (*msg_callback)(int, const char *, va_list, void *); void *msg_callback_data; }; diff --git a/aegisub/libass/ass_parse.c b/aegisub/libass/ass_parse.c new file mode 100644 index 000000000..0f3b4414a --- /dev/null +++ b/aegisub/libass/ass_parse.c @@ -0,0 +1,857 @@ +/* + * 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 "config.h" + +#include +#include +#include +#include + +#include "ass_render.h" +#include "ass_parse.h" + +#define MAX_BE 127 +#define NBSP 0xa0 // unicode non-breaking space character + +#define skip_to(x) while ((*p != (x)) && (*p != '}') && (*p != 0)) { ++p;} +#define skip(x) if (*p == (x)) ++p; else { return p; } +#define skipopt(x) if (*p == (x)) { ++p; } + +/** + * \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 void change_font_size(ASS_Renderer *render_priv, double sz) +{ + double size = sz * render_priv->font_scale; + + if (size < 1) + size = 1; + else if (size > render_priv->height * 2) + size = render_priv->height * 2; + + ass_font_set_size(render_priv->state.font, size); + + render_priv->state.font_size = sz; +} + +/** + * \brief Change current font, using setting from render_priv->state. + */ +void update_font(ASS_Renderer *render_priv) +{ + unsigned val; + ASS_FontDesc desc; + desc.family = strdup(render_priv->state.family); + desc.treat_family_as_pattern = + render_priv->state.treat_family_as_pattern; + + val = render_priv->state.bold; + // 0 = normal, 1 = bold, >1 = exact weight + if (val == 1 || val == -1) + val = 200; // bold + else if (val <= 0) + val = 80; // normal + desc.bold = val; + + val = render_priv->state.italic; + if (val == 1 || val == -1) + val = 110; // italic + else if (val <= 0) + val = 0; // normal + desc.italic = val; + + 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 + */ +void change_border(ASS_Renderer *render_priv, double border_x, + double border_y) +{ + int bord; + if (!render_priv->state.font) + return; + + if (border_x < 0 && border_y < 0) { + if (render_priv->state.style->BorderStyle == 1 || + render_priv->state.style->BorderStyle == 3) + border_x = border_y = render_priv->state.style->Outline; + else + border_x = border_y = 1.; + } + + 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(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) { + ass_msg(render_priv->library, MSGL_V, + "failed to get stroker"); + 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; + } +} + +/** + * \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) +{ + (*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 +inline 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); +} + +/** + * \brief Multiply two alpha values + * \param a first value + * \param b second value + * \return result of multiplication + * Parameters and result are limited by 0xFF. + */ +inline uint32_t mult_alpha(uint32_t a, uint32_t b) +{ + 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) +{ + 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; +} + +/** + * Parse a vector clip into an outline, using the proper scaling + * parameters. Translate it to correct for screen borders, if needed. + */ +static char *parse_vector_clip(ASS_Renderer *render_priv, char *p) +{ + int scale = 1; + int res = 0; + ASS_Drawing *drawing; + + render_priv->state.clip_drawing = ass_drawing_new( + render_priv->fontconfig_priv, + render_priv->state.font, + render_priv->settings.hinting, + render_priv->ftlibrary); + drawing = render_priv->state.clip_drawing; + skipopt('('); + res = mystrtoi(&p, &scale); + skipopt(',') + if (!res) + scale = 1; + drawing->scale = scale; + drawing->scale_x = render_priv->font_scale_x * render_priv->font_scale; + drawing->scale_y = render_priv->font_scale; + while (*p != ')' && *p != '}' && p != 0) + ass_drawing_add_char(drawing, *p++); + skipopt(')'); + ass_drawing_parse(drawing, 1); + + // We need to translate the clip according to screen borders + if (render_priv->settings.left_margin != 0 || + render_priv->settings.top_margin != 0) { + FT_Vector trans = { + .x = int_to_d6(render_priv->settings.left_margin), + .y = -int_to_d6(render_priv->settings.top_margin), + }; + FT_Outline_Translate(&drawing->glyph->outline, trans.x, trans.y); + } + ass_msg(render_priv->library, MSGL_DBG2, + "Parsed vector clip: scale %d, scales (%f, %f) string [%s]\n", + scale, drawing->scale_x, drawing->scale_y, drawing->text); + + return p; +} + +/** + * \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(ASS_Renderer *render_priv, char *p, double pwr) +{ + 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)) + 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; + char *start = p; + skipopt('('); + res &= mystrtoi(&p, &x0); + skipopt(','); + res &= mystrtoi(&p, &y0); + skipopt(','); + res &= mystrtoi(&p, &x1); + skipopt(','); + res &= mystrtoi(&p, &y1); + skipopt(')'); + 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 if (!render_priv->state.clip_drawing) { + p = parse_vector_clip(render_priv, start); + render_priv->state.clip_drawing_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(render_priv->library, 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(render_priv->library, MSGL_DBG2, + "movement: (%f, %f) -> (%f, %f)", 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; + int hex = render_priv->track->track_type == TRACK_TYPE_ASS; + if (strtocolor(render_priv->library, &p, &val, hex)) { + 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(render_priv->library, MSGL_DBG2, "an %d", val); + if (v != 0) + v = 3 - v; + val = ((val - 1) % 3) + 1; // horizontal alignment + val += v * 4; + ass_msg(render_priv->library, MSGL_DBG2, "align %d", 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) + // take care of a vsfilter quirk: handle illegal \a8 like \a5 + render_priv->state.alignment = (val == 8) ? 5 : 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(render_priv->library, MSGL_DBG2, "pos(%f, %f)", v1, v2); + if (render_priv->state.evt_type == EVENT_POSITIONED) { + ass_msg(render_priv->library, MSGL_V, "Subtitle has a new \\pos " + "after \\move or \\pos, ignoring"); + } 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(render_priv->library, MSGL_DBG2, "org(%d, %d)", v1, v2); + 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")) { + char *start = p; + int x0, y0, x1, y1; + int res = 1; + skipopt('('); + res &= mystrtoi(&p, &x0); + skipopt(','); + res &= mystrtoi(&p, &y0); + skipopt(','); + res &= mystrtoi(&p, &x1); + skipopt(','); + res &= mystrtoi(&p, &y1); + skipopt(')'); + 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; + // Might be a vector clip + } else if (!render_priv->state.clip_drawing) { + p = parse_vector_clip(render_priv, start); + render_priv->state.clip_drawing_mode = 0; + } 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; + int hex = render_priv->track->track_type == TRACK_TYPE_ASS; + if (!strtocolor(render_priv->library, &p, &val, hex)) + val = render_priv->state.style->PrimaryColour; + ass_msg(render_priv->library, MSGL_DBG2, "color: %X", 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; + int hex = render_priv->track->track_type == TRACK_TYPE_ASS; + assert((n >= '1') && (n <= '4')); + if (!strtocolor(render_priv->library, &p, &val, hex)) + 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(render_priv->library, MSGL_WARN, "Bad command: %c%c", + n, cmd); + break; + } + ass_msg(render_priv->library, MSGL_DBG2, "single c/a at %f: %c%c = %X", + 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; + } else if (mystrcmp(&p, "q")) { + int val; + if (!mystrtoi(&p, &val)) + val = render_priv->track->WrapStyle; + render_priv->state.wrap_style = val; + } + + return p; +} + +/** + * \brief Get next ucs4 char from string, parsing and executing style overrides + * \param str string pointer + * \return ucs4 code of the next char + * On return str points to the unparsed part of the string + */ +unsigned get_next_char(ASS_Renderer *render_priv, char **str) +{ + 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(render_priv->library, MSGL_V, + "Unable to parse: '%s'", p); + if (*p == 0) + break; + } + } + if (*p == '\t') { + ++p; + *str = p; + return ' '; + } + if (*p == '\\') { + if ((p[1] == 'N') || ((p[1] == 'n') && + (render_priv->state.wrap_style == 2))) { + p += 2; + *str = p; + return '\n'; + } else if (p[1] == 'n') { + p += 2; + *str = p; + return ' '; + } else if (p[1] == 'h') { + p += 2; + *str = p; + return NBSP; + } + } + chr = ass_utf8_get_char((char **) &p); + *str = p; + return chr; +} diff --git a/aegisub/libass/ass_parse.h b/aegisub/libass/ass_parse.h new file mode 100644 index 000000000..eac2baeef --- /dev/null +++ b/aegisub/libass/ass_parse.h @@ -0,0 +1,37 @@ +/* + * 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_PARSE_H +#define LIBASS_PARSE_H + +#define BLUR_MAX_RADIUS 100.0 + +#define _r(c) ((c) >> 24) +#define _g(c) (((c) >> 16) & 0xFF) +#define _b(c) (((c) >> 8) & 0xFF) +#define _a(c) ((c) & 0xFF) + +void update_font(ASS_Renderer *render_priv); +void change_border(ASS_Renderer *render_priv, double border_x, + double border_y); +unsigned get_next_char(ASS_Renderer *render_priv, char **str); +inline void change_alpha(uint32_t *var, uint32_t new, double pwr); +inline uint32_t mult_alpha(uint32_t a, uint32_t b); + + +#endif /* LIBASS_PARSE_H */ diff --git a/aegisub/libass/ass_render.c b/aegisub/libass/ass_render.c index f86c73e95..edb0c840a 100644 --- a/aegisub/libass/ass_render.c +++ b/aegisub/libass/ass_render.c @@ -37,224 +37,19 @@ #include "ass_fontconfig.h" #include "ass_library.h" #include "ass_drawing.h" +#include "ass_render.h" +#include "ass_parse.h" #define MAX_GLYPHS_INITIAL 1024 #define MAX_LINES_INITIAL 64 -#define BLUR_MAX_RADIUS 100.0 -#define MAX_BE 127 #define SUBPIXEL_MASK 63 #define SUBPIXEL_ACCURACY 7 // d6 mask for subpixel accuracy adjustment #define GLYPH_CACHE_MAX 1000 -#define BITMAP_CACHE_MAX_SIZE 50 * 1048576; +#define BITMAP_CACHE_MAX_SIZE 50 * 1048576 -static int last_render_id = 0; - -typedef struct double_bbox_s { - double xMin; - double xMax; - double yMin; - double yMax; -} double_bbox_t; - -typedef struct double_vector_s { - double x; - double y; -} double_vector_t; - -typedef struct free_list_s { - void *object; - struct free_list_s *next; -} free_list_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; -} event_images_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_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 { - double asc, desc; -} line_info_t; - -typedef struct text_info_s { - 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; - int flags; // decoration flags (underline/strike-through) - - 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 - ass_drawing_t *clip_drawing;// clip vector - int clip_drawing_mode; // 0 = regular clip, 1 = inverse clip - - 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; - -} render_context_t; - -typedef struct cache_store_s { - hashmap_t *font_cache; - hashmap_t *glyph_cache; - hashmap_t *bitmap_cache; - hashmap_t *composite_cache; - size_t glyph_max; - size_t bitmap_max_size; -} cache_store_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 - - // 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; - - free_list_t *free_head; - free_list_t *free_tail; -}; - -struct render_priv_s { - int top, height; - int render_id; -}; - -static void ass_lazy_track_init(ass_renderer_t *render_priv) +static void ass_lazy_track_init(ASS_Renderer *render_priv) { - ass_track_t *track = render_priv->track; - ass_settings_t *settings_priv = &render_priv->settings; + ASS_Track *track = render_priv->track; if (track->PlayResX && track->PlayResY) return; @@ -264,16 +59,12 @@ static void ass_lazy_track_init(ass_renderer_t *render_priv) 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(render_priv->library, MSGL_WARN, "PlayResY undefined, setting to %d", track->PlayResY); } else if (!track->PlayResY) { - track->PlayResY = track->PlayResX / orig_aspect + .5; + track->PlayResY = track->PlayResX * 3 / 4; ass_msg(render_priv->library, MSGL_WARN, "PlayResY undefined, setting to %d", track->PlayResY); } else if (!track->PlayResX && track->PlayResY == 1024) { @@ -281,18 +72,18 @@ static void ass_lazy_track_init(ass_renderer_t *render_priv) ass_msg(render_priv->library, MSGL_WARN, "PlayResX undefined, setting to %d", track->PlayResX); } else if (!track->PlayResX) { - track->PlayResX = track->PlayResY * orig_aspect + .5; + track->PlayResX = track->PlayResY * 4 / 3; ass_msg(render_priv->library, MSGL_WARN, "PlayResX undefined, setting to %d", track->PlayResX); } } } -ass_renderer_t *ass_renderer_init(ass_library_t *library) +ASS_Renderer *ass_renderer_init(ASS_Library *library) { int error; FT_Library ft; - ass_renderer_t *priv = 0; + ASS_Renderer *priv = 0; int vmajor, vminor, vpatch; error = FT_Init_FreeType(&ft); @@ -307,7 +98,7 @@ ass_renderer_t *ass_renderer_init(ass_library_t *library) ass_msg(library, MSGL_V, "FreeType headers version: %d.%d.%d", FREETYPE_MAJOR, FREETYPE_MINOR, FREETYPE_PATCH); - priv = calloc(1, sizeof(ass_renderer_t)); + priv = calloc(1, sizeof(ASS_Renderer)); if (!priv) { FT_Done_FreeType(ft); goto ass_init_exit; @@ -329,8 +120,8 @@ ass_renderer_t *ass_renderer_init(ass_library_t *library) 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)); + calloc(MAX_GLYPHS_INITIAL, sizeof(GlyphInfo)); + priv->text_info.lines = calloc(MAX_LINES_INITIAL, sizeof(LineInfo)); ass_init_exit: if (priv) @@ -341,7 +132,7 @@ ass_renderer_t *ass_renderer_init(ass_library_t *library) return priv; } -void ass_set_cache_limits(ass_renderer_t *render_priv, int glyph_max, +void ass_set_cache_limits(ASS_Renderer *render_priv, int glyph_max, int bitmap_max) { render_priv->cache.glyph_max = glyph_max ? glyph_max : GLYPH_CACHE_MAX; @@ -349,12 +140,12 @@ void ass_set_cache_limits(ass_renderer_t *render_priv, int glyph_max, BITMAP_CACHE_MAX_SIZE; } -static void free_list_clear(ass_renderer_t *render_priv) +static void free_list_clear(ASS_Renderer *render_priv) { if (render_priv->free_head) { - free_list_t *item = render_priv->free_head; + FreeList *item = render_priv->free_head; while(item) { - free_list_t *oi = item; + FreeList *oi = item; free(item->object); item = item->next; free(oi); @@ -363,12 +154,18 @@ static void free_list_clear(ass_renderer_t *render_priv) } } -void ass_renderer_done(ass_renderer_t *render_priv) +static void ass_free_images(ASS_Image *img); + +void ass_renderer_done(ASS_Renderer *render_priv) { 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); + + ass_free_images(render_priv->images_root); + ass_free_images(render_priv->prev_images_root); + if (render_priv->state.stroker) { FT_Stroker_Done(render_priv->state.stroker); render_priv->state.stroker = 0; @@ -381,7 +178,6 @@ void ass_renderer_done(ass_renderer_t *render_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); @@ -389,17 +185,18 @@ void ass_renderer_done(ass_renderer_t *render_priv) free(render_priv->settings.default_family); free_list_clear(render_priv); + free(render_priv); } /** - * \brief Create a new ass_image_t - * Parameters are the same as ass_image_t fields. + * \brief Create a new ASS_Image + * Parameters are the same as ASS_Image 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 *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)); + ASS_Image *img = calloc(1, sizeof(ASS_Image)); img->w = bitmap_w; img->h = bitmap_h; @@ -412,18 +209,11 @@ static ass_image_t *my_draw_bitmap(unsigned char *bitmap, int bitmap_w, 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; +static double x2scr_pos(ASS_Renderer *render_priv, double x); +static double y2scr_pos(ASS_Renderer *render_priv, double y); /* - * \brief Convert bitmap glyphs into ass_image_t list with inverse clipping + * \brief Convert bitmap glyphs into ASS_Image list with inverse clipping * * Inverse clipping with the following strategy: * - find rectangle from (x0, y0) to (cx0, y1) @@ -435,14 +225,14 @@ typedef struct rect_s { * 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) +static ASS_Image **render_glyph_i(ASS_Renderer *render_priv, + Bitmap *bm, int dst_x, int dst_y, + uint32_t color, uint32_t color2, int brk, + ASS_Image **tail) { int i, j, x0, y0, x1, y1, cx0, cy0, cx1, cy1, sx, sy, zx, zy; - rect_t r[4]; - ass_image_t *img; + Rect r[4]; + ASS_Image *img; dst_x += bm->left; dst_y += bm->top; @@ -522,7 +312,7 @@ static ass_image_t **render_glyph_i(ass_renderer_t *render_priv, } /** - * \brief convert bitmap glyph into ass_image_t struct(s) + * \brief convert bitmap glyph into ASS_Image struct(s) * \param bit freetype bitmap glyph, FT_PIXEL_MODE_GRAY * \param dst_x bitmap x coordinate in video frame * \param dst_y bitmap y coordinate in video frame @@ -533,10 +323,9 @@ static ass_image_t **render_glyph_i(ass_renderer_t *render_priv, * \return pointer to the new list tail * Performs clipping. Uses my_draw_bitmap for actual bitmap convertion. */ -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) +static ASS_Image ** +render_glyph(ASS_Renderer *render_priv, Bitmap *bm, int dst_x, int dst_y, + uint32_t color, uint32_t color2, int brk, ASS_Image **tail) { // Inverse clipping in use? if (render_priv->state.clip_mode) @@ -549,17 +338,17 @@ static ass_image_t **render_glyph(ass_renderer_t *render_priv, 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; + ASS_Image *img; 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; + clip_x0 = FFMINMAX(render_priv->state.clip_x0, 0, render_priv->width); + clip_y0 = FFMINMAX(render_priv->state.clip_y0, 0, render_priv->height); + clip_x1 = FFMINMAX(render_priv->state.clip_x1, 0, render_priv->width); + clip_y1 = FFMINMAX(render_priv->state.clip_y1, 0, render_priv->height); b_x0 = 0; b_y0 = 0; b_x1 = bm->w; @@ -610,24 +399,37 @@ static ass_image_t **render_glyph(ass_renderer_t *render_priv, return tail; } +/** + * \brief Replace the bitmap buffer in ASS_Image with a copy + * \param img ASS_Image to operate on + * \return pointer to old bitmap buffer + */ +static unsigned char *clone_bitmap_buffer(ASS_Image *img) +{ + unsigned char *old_bitmap = img->bitmap; + int size = img->stride * (img->h - 1) + img->w; + img->bitmap = malloc(size); + memcpy(img->bitmap, old_bitmap, size); + return old_bitmap; +} + /** * \brief Calculate overlapping area of two consecutive bitmaps and in case they - * overlap, composite them together + * overlap, blend them together * 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_renderer_t *render_priv, ass_image_t **last_tail, - ass_image_t **tail, bitmap_hash_key_t *last_hash, - bitmap_hash_key_t *hash) +render_overlap(ASS_Renderer *render_priv, ASS_Image **last_tail, + ASS_Image **tail) { 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; + CompositeHashKey hk; + CompositeHashValue *hv; + CompositeHashValue chv; int ax = (*last_tail)->dst_x; int ay = (*last_tail)->dst_y; int aw = (*last_tail)->w; @@ -663,8 +465,8 @@ render_overlap(ass_renderer_t *render_priv, ass_image_t **last_tail, // Query cache memset(&hk, 0, sizeof(hk)); - memcpy(&hk.a, last_hash, sizeof(*last_hash)); - memcpy(&hk.b, hash, sizeof(*hash)); + hk.a = (*last_tail)->bitmap; + hk.b = (*tail)->bitmap; hk.aw = aw; hk.ah = ah; hk.bw = bw; @@ -673,6 +475,8 @@ render_overlap(ass_renderer_t *render_priv, ass_image_t **last_tail, hk.ay = ay; hk.bx = bx; hk.by = by; + hk.as = as; + hk.bs = bs; hv = cache_find_composite(render_priv->cache.composite_cache, &hk); if (hv) { (*last_tail)->bitmap = hv->a; @@ -680,19 +484,15 @@ render_overlap(ass_renderer_t *render_priv, ass_image_t **last_tail, 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); + a = clone_bitmap_buffer(*last_tail); + b = clone_bitmap_buffer(*tail); - // Composite overlapping area + // Blend 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]; + m = FFMIN(a[opos] + b[cpos], 0xff); (*last_tail)->bitmap[opos] = 0; (*tail)->bitmap[cpos] = m; } @@ -703,14 +503,14 @@ render_overlap(ass_renderer_t *render_priv, ass_image_t **last_tail, cache_add_composite(render_priv->cache.composite_cache, &hk, &chv); } -static void free_list_add(ass_renderer_t *render_priv, void *object) +static void free_list_add(ASS_Renderer *render_priv, void *object) { if (!render_priv->free_head) { - render_priv->free_head = calloc(1, sizeof(free_list_t)); + render_priv->free_head = calloc(1, sizeof(FreeList)); render_priv->free_head->object = object; render_priv->free_tail = render_priv->free_head; } else { - free_list_t *l = calloc(1, sizeof(free_list_t)); + FreeList *l = calloc(1, sizeof(FreeList)); l->object = object; render_priv->free_tail->next = l; render_priv->free_tail = render_priv->free_tail->next; @@ -722,20 +522,26 @@ static void free_list_add(ass_renderer_t *render_priv, void *object) * applicable. The blended bitmaps are added to a free list which is freed * at the start of a new frame. */ -static void blend_vector_clip(ass_renderer_t *render_priv, - ass_image_t *head) +static void blend_vector_clip(ASS_Renderer *render_priv, + ASS_Image *head) { FT_Glyph glyph; FT_BitmapGlyph clip_bm; - ass_image_t *cur; - ass_drawing_t *drawing = render_priv->state.clip_drawing; + ASS_Image *cur; + ASS_Drawing *drawing = render_priv->state.clip_drawing; + int error; if (!drawing) return; // Rasterize it FT_Glyph_Copy((FT_Glyph) drawing->glyph, &glyph); - FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1); + error = FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1); + if (error) { + ass_msg(render_priv->library, MSGL_V, + "Clip vector rasterization failed: %d. Skipping.", error); + goto blend_vector_exit; + } clip_bm = (FT_BitmapGlyph) glyph; clip_bm->top = -clip_bm->top; @@ -786,7 +592,7 @@ static void blend_vector_clip(ass_renderer_t *render_priv, free_list_add(render_priv, nbuffer); // Blend together - memcpy(nbuffer, abuffer, as * ah); + memcpy(nbuffer, abuffer, as * (ah - 1) + aw); for (y = 0; y < h; y++) for (x = 0; x < w; x++) { apos = (atop + y) * as + aleft + x; @@ -818,31 +624,31 @@ static void blend_vector_clip(ass_renderer_t *render_priv, // Free clip vector and its bitmap, we don't need it anymore FT_Done_Glyph(glyph); +blend_vector_exit: ass_drawing_free(render_priv->state.clip_drawing); render_priv->state.clip_drawing = 0; } /** - * \brief Convert text_info_t struct to ass_image_t list + * \brief Convert TextInfo struct to ASS_Image list * Splits glyphs in halves when needed (for \kf karaoke). */ -static ass_image_t *render_text(ass_renderer_t *render_priv, int dst_x, +static ASS_Image *render_text(ASS_Renderer *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; - text_info_t *text_info = &render_priv->text_info; + Bitmap *bm; + ASS_Image *head; + ASS_Image **tail = &head; + ASS_Image **last_tail = 0; + ASS_Image **here_tail = 0; + TextInfo *text_info = &render_priv->text_info; for (i = 0; i < text_info->length; ++i) { - glyph_info_t *info = text_info->glyphs + i; + GlyphInfo *info = text_info->glyphs + i; if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_s - || (info->shadow_x == 0 && info->shadow_y == 0)) + || (info->shadow_x == 0 && info->shadow_y == 0) || info->skip) continue; pen_x = @@ -858,16 +664,16 @@ static ass_image_t *render_text(ass_renderer_t *render_priv, int dst_x, 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); + render_overlap(render_priv, last_tail, here_tail); + 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) + GlyphInfo *info = text_info->glyphs + i; + if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm_o + || info->skip) continue; pen_x = dst_x + (info->pos.x >> 6); @@ -875,7 +681,7 @@ static ass_image_t *render_text(ass_renderer_t *render_priv, int dst_x, bm = info->bm_o; if ((info->effect_type == EF_KARAOKE_KO) - && (info->effect_timing <= info->bbox.xMax)) { + && (info->effect_timing <= (info->bbox.xMax >> 6))) { // do nothing } else { here_tail = tail; @@ -883,15 +689,16 @@ static ass_image_t *render_text(ass_renderer_t *render_priv, int dst_x, 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); + render_overlap(render_priv, last_tail, here_tail); + 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) + GlyphInfo *info = text_info->glyphs + i; + if ((info->symbol == 0) || (info->symbol == '\n') || !info->bm + || info->skip) continue; pen_x = dst_x + (info->pos.x >> 6); @@ -900,7 +707,7 @@ static ass_image_t *render_text(ass_renderer_t *render_priv, int dst_x, if ((info->effect_type == EF_KARAOKE) || (info->effect_type == EF_KARAOKE_KO)) { - if (info->effect_timing > info->bbox.xMax) + if (info->effect_timing > (info->bbox.xMax >> 6)) tail = render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], 0, 1000000, tail); @@ -927,13 +734,13 @@ static ass_image_t *render_text(ass_renderer_t *render_priv, int dst_x, /** * \brief Mapping between script and screen coordinates */ -static double x2scr(ass_renderer_t *render_priv, double x) +static double x2scr(ASS_Renderer *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(ass_renderer_t *render_priv, double x) +static double x2scr_pos(ASS_Renderer *render_priv, double x) { return x * render_priv->orig_width / render_priv->track->PlayResX + render_priv->settings.left_margin; @@ -942,20 +749,20 @@ static double x2scr_pos(ass_renderer_t *render_priv, double x) /** * \brief Mapping between script and screen coordinates */ -static double y2scr(ass_renderer_t *render_priv, double y) +static double y2scr(ASS_Renderer *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(ass_renderer_t *render_priv, double y) +static double y2scr_pos(ASS_Renderer *render_priv, double y) { return y * render_priv->orig_height / render_priv->track->PlayResY + render_priv->settings.top_margin; } // the same for toptitles -static double y2scr_top(ass_renderer_t *render_priv, double y) +static double y2scr_top(ASS_Renderer *render_priv, double y) { if (render_priv->settings.use_margins) return y * render_priv->orig_height_nocrop / @@ -967,7 +774,7 @@ static double y2scr_top(ass_renderer_t *render_priv, double y) } // the same for subtitles -static double y2scr_sub(ass_renderer_t *render_priv, double y) +static double y2scr_sub(ASS_Renderer *render_priv, double y) { if (render_priv->settings.use_margins) return y * render_priv->orig_height_nocrop / @@ -980,7 +787,7 @@ static double y2scr_sub(ass_renderer_t *render_priv, double y) FFMAX(render_priv->settings.top_margin, 0); } -static void compute_string_bbox(text_info_t *info, double_bbox_t *bbox) +static void compute_string_bbox(TextInfo *info, DBBox *bbox) { int i; @@ -992,6 +799,7 @@ static void compute_string_bbox(text_info_t *info, double_bbox_t *bbox) d6_to_double(info->glyphs[0].pos.y); for (i = 0; i < info->length; ++i) { + if (info->glyphs[i].skip) continue; 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); @@ -1001,832 +809,8 @@ static void compute_string_bbox(text_info_t *info, double_bbox_t *bbox) 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 void change_font_size(ass_renderer_t *render_priv, double sz) -{ - double size = sz * render_priv->font_scale; - - if (size < 1) - size = 1; - else if (size > render_priv->height * 2) - size = render_priv->height * 2; - - ass_font_set_size(render_priv->state.font, size); - - render_priv->state.font_size = sz; -} - -/** - * \brief Change current font, using setting from render_priv->state. - */ -static void update_font(ass_renderer_t *render_priv) -{ - 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_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_priv->state.italic; - if (val == 0) - val = 0; // normal - else if (val == 1) - val = 110; //italic - desc.italic = val; - - 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(ass_renderer_t *render_priv, double border_x, - double border_y) -{ - int bord; - if (!render_priv->state.font) - return; - - 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.; - } - - 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(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) { - ass_msg(render_priv->library, MSGL_V, - "failed to get stroker"); - 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) -#define _g(c) (((c)>>16)&0xFF) -#define _b(c) (((c)>>8)&0xFF) -#define _a(c) ((c)&0xFF) - -/** - * \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) -{ - (*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) -{ - *var = - (_r(*var) << 24) + (_g(*var) << 16) + (_b(*var) << 8) + - (_a(*var) * (1 - pwr) + _a(new) * pwr); -} - -/** - * \brief Multiply two alpha values - * \param a first value - * \param b second value - * \return result of multiplication - * Parameters and result are limited by 0xFF. - */ -static uint32_t mult_alpha(uint32_t a, uint32_t b) -{ - 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) -{ - 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; -} - -#define skip_to(x) while ((*p != (x)) && (*p != '}') && (*p != 0)) { ++p;} -#define skip(x) if (*p == (x)) ++p; else { return p; } -#define skipopt(x) if (*p == (x)) { ++p; } - -/** - * Parse a vector clip into an outline, using the proper scaling - * parameters. Translate it to correct for screen borders, if needed. - */ -static char *parse_vector_clip(ass_renderer_t *render_priv, char *p) -{ - int scale = 1; - int res = 0; - ass_drawing_t *drawing; - render_priv->state.clip_drawing = ass_drawing_new( - render_priv->fontconfig_priv, - render_priv->state.font, - render_priv->settings.hinting, - render_priv->ftlibrary); - drawing = render_priv->state.clip_drawing; - skipopt('('); - res = mystrtoi(&p, &scale); - skipopt(',') - if (!res) - scale = 1; - drawing->scale = scale; - drawing->scale_x = render_priv->font_scale_x * render_priv->font_scale; - drawing->scale_y = render_priv->font_scale; - while (*p != ')' && *p != '}' && p != 0) - ass_drawing_add_char(drawing, *p++); - skipopt(')'); - ass_drawing_parse(drawing, 1); - // We need to translate the clip according to screen borders - if (render_priv->settings.left_margin != 0 || - render_priv->settings.top_margin != 0) { - FT_Vector trans = { - .x = int_to_d6(render_priv->settings.left_margin), - .y = -int_to_d6(render_priv->settings.top_margin), - }; - FT_Outline_Translate(&drawing->glyph->outline, trans.x, trans.y); - } - ass_msg(render_priv->library, MSGL_DBG2, - "Parsed vector clip: scale %d, scales (%f, %f) string [%s]\n", - scale, drawing->scale_x, drawing->scale_y, drawing->text); - - return p; -} - -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(ass_renderer_t *render_priv, char *p, double pwr) -{ - 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)) - 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; - char *start = p; - skipopt('('); - res &= mystrtoi(&p, &x0); - skipopt(','); - res &= mystrtoi(&p, &y0); - skipopt(','); - res &= mystrtoi(&p, &x1); - skipopt(','); - res &= mystrtoi(&p, &y1); - skipopt(')'); - 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 if (!render_priv->state.clip_drawing) { - p = parse_vector_clip(render_priv, start); - render_priv->state.clip_drawing_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(render_priv->library, 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(render_priv->library, MSGL_DBG2, - "movement: (%f, %f) -> (%f, %f)", 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(render_priv->library, &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(render_priv->library, MSGL_DBG2, "an %d", val); - if (v != 0) - v = 3 - v; - val = ((val - 1) % 3) + 1; // horizontal alignment - val += v * 4; - ass_msg(render_priv->library, MSGL_DBG2, "align %d", 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(render_priv->library, MSGL_DBG2, "pos(%f, %f)", v1, v2); - if (render_priv->state.evt_type == EVENT_POSITIONED) { - ass_msg(render_priv->library, MSGL_V, "Subtitle has a new \\pos " - "after \\move or \\pos, ignoring"); - } 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(render_priv->library, MSGL_DBG2, "org(%d, %d)", v1, v2); - 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")) { - char *start = p; - int x0, y0, x1, y1; - int res = 1; - skipopt('('); - res &= mystrtoi(&p, &x0); - skipopt(','); - res &= mystrtoi(&p, &y0); - skipopt(','); - res &= mystrtoi(&p, &x1); - skipopt(','); - res &= mystrtoi(&p, &y1); - skipopt(')'); - 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; - // Might be a vector clip - } else if (!render_priv->state.clip_drawing) { - p = parse_vector_clip(render_priv, start); - render_priv->state.clip_drawing_mode = 0; - } 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(render_priv->library, &p, &val)) - val = render_priv->state.style->PrimaryColour; - ass_msg(render_priv->library, MSGL_DBG2, "color: %X", 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(render_priv->library, &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(render_priv->library, MSGL_WARN, "Bad command: %c%c", - n, cmd); - break; - } - ass_msg(render_priv->library, MSGL_DBG2, "single c/a at %f: %c%c = %X", - 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 skipopt -#undef skip_to -} - -/** - * \brief Get next ucs4 char from string, parsing and executing style overrides - * \param str string pointer - * \return ucs4 code of the next char - * On return str points to the unparsed part of the string - */ -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(render_priv, p, 1.); - if (*p == '}') { // end of tag - p++; - if (*p == '{') { - p++; - continue; - } else - break; - } else if (*p != '\\') - ass_msg(render_priv->library, MSGL_V, - "Unable to parse: '%s'", 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_renderer_t *render_priv, ass_event_t *event) +apply_transition_effects(ASS_Renderer *render_priv, ASS_Event *event) { int v[4]; int cnt; @@ -1905,7 +889,7 @@ apply_transition_effects(ass_renderer_t *render_priv, ass_event_t *event) * \brief partially reset render_context to style values * Works like {\r}: resets some style overrides */ -static void reset_render_context(ass_renderer_t *render_priv) +void reset_render_context(ASS_Renderer *render_priv) { render_priv->state.c[0] = render_priv->state.style->PrimaryColour; render_priv->state.c[1] = render_priv->state.style->SecondaryColour; @@ -1916,8 +900,8 @@ static void reset_render_context(ass_renderer_t *render_priv) (render_priv->state.style->StrikeOut ? DECO_STRIKETHROUGH : 0); render_priv->state.font_size = render_priv->state.style->FontSize; - if (render_priv->state.family) - free(render_priv->state.family); + free(render_priv->state.family); + render_priv->state.family = NULL; 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; @@ -1936,6 +920,7 @@ static void reset_render_context(ass_renderer_t *render_priv) 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.; + render_priv->state.wrap_style = render_priv->track->WrapStyle; // FIXME: does not reset unsupported attributes. } @@ -1944,7 +929,7 @@ static void reset_render_context(ass_renderer_t *render_priv) * \brief Start new event. Reset render_priv->state. */ static void -init_render_context(ass_renderer_t *render_priv, ass_event_t *event) +init_render_context(ASS_Renderer *render_priv, ASS_Event *event) { render_priv->state.event = event; render_priv->state.style = render_priv->track->styles + event->Style; @@ -1977,9 +962,13 @@ init_render_context(ass_renderer_t *render_priv, ass_event_t *event) apply_transition_effects(render_priv, event); } -static void free_render_context(ass_renderer_t *render_priv) +static void free_render_context(ASS_Renderer *render_priv) { + free(render_priv->state.family); ass_drawing_free(render_priv->state.drawing); + + render_priv->state.family = NULL; + render_priv->state.drawing = NULL; } // Calculate the cbox of a series of points @@ -2064,11 +1053,69 @@ static void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x, free(valid_cont); } +/* + * Replace the outline of a glyph by a contour which makes up a simple + * opaque rectangle. + */ +static void draw_opaque_box(ASS_Renderer *render_priv, uint32_t ch, + FT_Glyph glyph, int sx, int sy) +{ + int asc = 0, desc = 0; + int i; + int adv = d16_to_d6(glyph->advance.x); + double scale_y = render_priv->state.scale_y; + double scale_x = render_priv->state.scale_x + * render_priv->font_scale_x; + FT_OutlineGlyph og = (FT_OutlineGlyph) glyph; + FT_Outline *ol; + + // to avoid gaps + sx = FFMAX(64, sx); + sy = FFMAX(64, sy); + + if (ch == -1) { + asc = render_priv->state.drawing->asc; + desc = render_priv->state.drawing->desc; + } else { + ass_font_get_asc_desc(render_priv->state.font, ch, &asc, &desc); + asc *= scale_y; + desc *= scale_y; + } + + // Emulate the WTFish behavior of VSFilter, i.e. double-scale + // the sizes of the opaque box. + adv += double_to_d6(render_priv->state.hspacing * render_priv->font_scale + * scale_x); + adv *= scale_x; + sx *= scale_x; + sy *= scale_y; + desc *= scale_y; + desc += asc * (scale_y - 1.0); + + FT_Vector points[4] = { + { .x = -sx, .y = asc + sy }, + { .x = adv + sx, .y = asc + sy }, + { .x = adv + sx, .y = -desc - sy }, + { .x = -sx, .y = -desc - sy }, + }; + + FT_Outline_Done(render_priv->ftlibrary, &og->outline); + FT_Outline_New(render_priv->ftlibrary, 4, 1, &og->outline); + + ol = &og->outline; + ol->n_points = ol->n_contours = 0; + 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; +} + /* * 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, +static void stroke_outline_glyph(ASS_Renderer *render_priv, FT_OutlineGlyph *glyph, int sx, int sy) { if (sx <= 0 && sy <= 0) @@ -2111,27 +1158,25 @@ static void stroke_outline_glyph(ass_renderer_t *render_priv, * \brief Get normal and outline (border) glyphs * \param symbol ucs4 char * \param info out: struct filled with extracted data - * \param advance subpixel shift vector used for cache lookup * Tries to get both glyphs from cache. * If they can't be found, gets a glyph from font face, generates outline with FT_Stroker, * and add them to cache. * The glyphs are returned in info->glyph and info->outline_glyph */ static void -get_outline_glyph(ass_renderer_t *render_priv, int symbol, - glyph_info_t *info, FT_Vector *advance, - ass_drawing_t *drawing) +get_outline_glyph(ASS_Renderer *render_priv, int symbol, GlyphInfo *info, + ASS_Drawing *drawing) { - glyph_hash_val_t *val; - glyph_hash_key_t key; + GlyphHashValue *val; + GlyphHashKey key; memset(&key, 0, sizeof(key)); 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.border_style = render_priv->state.style->BorderStyle; key.drawing_hash = drawing->hash; } else { key.font = render_priv->state.font; @@ -2141,12 +1186,12 @@ get_outline_glyph(ass_renderer_t *render_priv, int symbol, 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; + key.border_style = render_priv->state.style->BorderStyle; } - memset(info, 0, sizeof(glyph_info_t)); + memset(info, 0, sizeof(GlyphInfo)); val = cache_find_glyph(render_priv->cache.glyph_cache, &key); if (val) { @@ -2161,7 +1206,7 @@ get_outline_glyph(ass_renderer_t *render_priv, int symbol, drawing->desc = val->desc; } } else { - glyph_hash_val_t v; + GlyphHashValue v; if (drawing->hash) { ass_drawing_parse(drawing, 0); FT_Glyph_Copy((FT_Glyph) drawing->glyph, &info->glyph); @@ -2178,8 +1223,17 @@ get_outline_glyph(ass_renderer_t *render_priv, int symbol, info->advance.y = d16_to_d6(info->glyph->advance.y); FT_Glyph_Get_CBox(info->glyph, FT_GLYPH_BBOX_SUBPIXELS, &info->bbox); - if (render_priv->state.border_x > 0 || - render_priv->state.border_y > 0) { + if (render_priv->state.style->BorderStyle == 3 && + (render_priv->state.border_x > 0|| + render_priv->state.border_y > 0)) { + FT_Glyph_Copy(info->glyph, &info->outline_glyph); + draw_opaque_box(render_priv, symbol, 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)); + } else if (render_priv->state.border_x > 0 || + render_priv->state.border_y > 0) { FT_Glyph_Copy(info->glyph, &info->outline_glyph); stroke_outline_glyph(render_priv, @@ -2206,7 +1260,8 @@ get_outline_glyph(ass_renderer_t *render_priv, int symbol, 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); + double frz, double fax, double fay, double scale, + int yshift); /** * \brief Get bitmaps for a glyph @@ -2217,10 +1272,10 @@ static void transform_3d(FT_Vector shift, FT_Glyph *glyph, * They are returned in info->bm (glyph), info->bm_o (outline) and info->bm_s (shadow). */ static void -get_bitmap_glyph(ass_renderer_t *render_priv, glyph_info_t *info) +get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info) { - bitmap_hash_val_t *val; - bitmap_hash_key_t *key = &info->hash_key; + BitmapHashValue *val; + BitmapHashKey *key = &info->hash_key; val = cache_find_bitmap(render_priv->cache.bitmap_cache, key); @@ -2230,17 +1285,22 @@ get_bitmap_glyph(ass_renderer_t *render_priv, glyph_info_t *info) info->bm_s = val->bm_s; } else { FT_Vector shift; - bitmap_hash_val_t hash_val; + BitmapHashValue hash_val; int error; + double fax_scaled, fay_scaled; info->bm = info->bm_o = info->bm_s = 0; - if (info->glyph && info->symbol != '\n' && info->symbol != 0) { + if (info->glyph && info->symbol != '\n' && info->symbol != 0 + && !info->skip) { // 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; + fax_scaled = info->fax * render_priv->font_scale_x * + render_priv->state.scale_x; + fay_scaled = info->fay * render_priv->state.scale_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); + info->frx, info->fry, info->frz, fax_scaled, + fay_scaled, render_priv->font_scale, info->asc); // subpixel shift if (info->glyph) @@ -2261,7 +1321,8 @@ get_bitmap_glyph(ass_renderer_t *render_priv, glyph_info_t *info) &info->bm, &info->bm_o, &info->bm_s, info->be, info->blur * render_priv->border_scale, - info->hash_key.shadow_offset); + info->hash_key.shadow_offset, + info->hash_key.border_style); if (error) info->symbol = 0; @@ -2288,12 +1349,12 @@ get_bitmap_glyph(ass_renderer_t *render_priv, glyph_info_t *info) * lines[].asc * lines[].desc */ -static void measure_text(ass_renderer_t *render_priv) +static void measure_text(ASS_Renderer *render_priv) { - text_info_t *text_info = &render_priv->text_info; + TextInfo *text_info = &render_priv->text_info; int cur_line = 0; double max_asc = 0., max_desc = 0.; - glyph_info_t *last = NULL; + GlyphInfo *last = NULL; int i; int empty_line = 1; text_info->height = 0.; @@ -2312,7 +1373,7 @@ static void measure_text(ass_renderer_t *render_priv) } else empty_line = 0; if (i < text_info->length) { - glyph_info_t *cur = text_info->glyphs + i; + GlyphInfo *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) @@ -2326,6 +1387,61 @@ static void measure_text(ass_renderer_t *render_priv) 1) * render_priv->settings.line_spacing; } +/** + * Mark extra whitespace for later removal. + */ +#define IS_WHITESPACE(x) ((x->symbol == ' ' || x->symbol == '\n') \ + && !x->linebreak) +static void trim_whitespace(ASS_Renderer *render_priv) +{ + int i, j; + GlyphInfo *cur; + TextInfo *ti = &render_priv->text_info; + + // Mark trailing spaces + i = ti->length - 1; + cur = ti->glyphs + i; + while (i && IS_WHITESPACE(cur)) { + cur->skip++; + cur = ti->glyphs + --i; + } + + // Mark leading whitespace + i = 0; + cur = ti->glyphs; + while (i < ti->length && IS_WHITESPACE(cur)) { + cur->skip++; + cur = ti->glyphs + ++i; + } + + // Mark all extraneous whitespace inbetween + for (i = 0; i < ti->length; ++i) { + cur = ti->glyphs + i; + if (cur->linebreak) { + // Mark whitespace before + j = i - 1; + cur = ti->glyphs + j; + while (j && IS_WHITESPACE(cur)) { + cur->skip++; + cur = ti->glyphs + --j; + } + // A break itself can contain a whitespace, too + cur = ti->glyphs + i; + if (cur->symbol == ' ') + cur->skip++; + // Mark whitespace after + j = i + 1; + cur = ti->glyphs + j; + while (j < ti->length && IS_WHITESPACE(cur)) { + cur->skip++; + cur = ti->glyphs + ++j; + } + i = j - 1; + } + } +} +#undef IS_WHITESPACE + /** * \brief rearrange text between lines * \param max_text_width maximal text line width in pixels @@ -2334,19 +1450,21 @@ static void measure_text(ass_renderer_t *render_priv) * 2. Try moving words from the end of a line to the beginning of the next one while it reduces * the difference in lengths between this two lines. * The result may not be optimal, but usually is good enough. + * + * FIXME: implement style 0 and 3 correctly, add support for style 1 */ static void -wrap_lines_smart(ass_renderer_t *render_priv, double max_text_width) +wrap_lines_smart(ASS_Renderer *render_priv, double max_text_width) { int i; - glyph_info_t *cur, *s1, *e1, *s2, *s3, *w; + GlyphInfo *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; + TextInfo *text_info = &render_priv->text_info; last_space = -1; text_info->n_lines = 1; @@ -2368,7 +1486,7 @@ wrap_lines_smart(ass_renderer_t *render_priv, double max_text_width) } if ((len >= max_text_width) - && (render_priv->track->WrapStyle != 2)) { + && (render_priv->state.wrap_style != 2)) { break_type = 1; break_at = last_space; if (break_at == -1) @@ -2388,7 +1506,7 @@ wrap_lines_smart(ass_renderer_t *render_priv, double max_text_width) // Raise maximum number of lines text_info->max_lines *= 2; text_info->lines = realloc(text_info->lines, - sizeof(line_info_t) * + sizeof(LineInfo) * text_info->max_lines); } if (lead < text_info->length) @@ -2409,7 +1527,7 @@ wrap_lines_smart(ass_renderer_t *render_priv, double max_text_width) } #define DIFF(x,y) (((x) < (y)) ? (y - x) : (x - y)) exit = 0; - while (!exit) { + while (!exit && render_priv->state.wrap_style != 1) { exit = 1; w = s3 = text_info->glyphs; s1 = s2 = 0; @@ -2463,13 +1581,23 @@ wrap_lines_smart(ass_renderer_t *render_priv, double max_text_width) #undef DIFF measure_text(render_priv); + trim_whitespace(render_priv); pen_shift_x = 0.; pen_shift_y = 0.; cur_line = 1; + + i = 0; + cur = text_info->glyphs + i; + while (i < text_info->length && cur->skip) + cur = text_info->glyphs + ++i; + pen_shift_x = d6_to_double(-cur->pos.x); + for (i = 0; i < text_info->length; ++i) { cur = text_info->glyphs + i; if (cur->linebreak) { + while (i < text_info->length && cur->skip && cur->symbol != '\n') + cur = text_info->glyphs + ++i; double height = text_info->lines[cur_line - 1].desc + text_info->lines[cur_line].asc; @@ -2496,11 +1624,11 @@ wrap_lines_smart(ass_renderer_t *render_priv, double max_text_width) * 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(ass_renderer_t *render_priv) +static void process_karaoke_effects(ASS_Renderer *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 + GlyphInfo *cur, *cur2; + GlyphInfo *s1, *e1; // start and end of the current word + GlyphInfo *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 @@ -2561,7 +1689,7 @@ static void process_karaoke_effects(ass_renderer_t *render_priv) * \param alignment alignment * \param bx, by out: base point coordinates */ -static void get_base_point(double_bbox_t *bbox, int alignment, double *bx, double *by) +static void get_base_point(DBBox *bbox, int alignment, double *bx, double *by) { const int halign = alignment & 3; const int valign = alignment & 12; @@ -2597,9 +1725,9 @@ static void get_base_point(double_bbox_t *bbox, int alignment, double *bx, doubl * onto the screen plane. */ static void -transform_3d_points(FT_Vector shift, FT_Glyph glyph, double frx, - double fry, double frz, double fax, double fay, - double scale) +transform_3d_points(FT_Vector shift, FT_Glyph glyph, double frx, double fry, + double frz, double fax, double fay, double scale, + int yshift) { double sx = sin(frx); double sy = sin(fry); @@ -2614,7 +1742,7 @@ transform_3d_points(FT_Vector shift, FT_Glyph glyph, double frx, dist = 20000 * scale; for (i = 0; i < outline->n_points; i++) { - x = (double) p[i].x + shift.x + (-fax * p[i].y); + x = (double) p[i].x + shift.x + (fax * (yshift - p[i].y)); y = (double) p[i].y + shift.y + (-fay * p[i].x); z = 0.; @@ -2652,18 +1780,18 @@ transform_3d_points(FT_Vector shift, FT_Glyph glyph, double frx, 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) + double scale, int yshift) { 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); + fax, fay, scale, yshift); if (glyph2 && *glyph2) transform_3d_points(shift, *glyph2, frx, fry, frz, - fax, fay, scale); + fax, fay, scale, yshift); } } @@ -2672,27 +1800,26 @@ transform_3d(FT_Vector shift, FT_Glyph *glyph, FT_Glyph *glyph2, * \brief Main ass rendering function, glues everything together * \param event event to render * \param event_images struct containing resulting images, will also be initialized - * Process event, appending resulting ass_image_t's to images_root. + * Process event, appending resulting ASS_Image's to images_root. */ static int -ass_render_event(ass_renderer_t *render_priv, ass_event_t *event, - event_images_t *event_images) +ass_render_event(ASS_Renderer *render_priv, ASS_Event *event, + EventImages *event_images) { char *p; FT_UInt previous; FT_UInt num_glyphs; FT_Vector pen; unsigned code; - double_bbox_t bbox; + DBBox 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; + TextInfo *text_info = &render_priv->text_info; + ASS_Drawing *drawing; if (event->Style >= render_priv->track->n_styles) { ass_msg(render_priv->library, MSGL_WARN, "No style found"); @@ -2748,7 +1875,7 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event, text_info->max_glyphs *= 2; text_info->glyphs = realloc(text_info->glyphs, - sizeof(glyph_info_t) * text_info->max_glyphs); + sizeof(GlyphInfo) * text_info->max_glyphs); } // Add kerning to pen @@ -2757,27 +1884,44 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event, 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; + pen.x += delta.x * render_priv->state.scale_x + * render_priv->font_scale_x; + pen.y += delta.y * render_priv->state.scale_y + * render_priv->font_scale_x; } ass_font_set_transform(render_priv->state.font, render_priv->state.scale_x * render_priv->font_scale_x, - render_priv->state.scale_y, &shift); + render_priv->state.scale_y, NULL); get_outline_glyph(render_priv, code, - text_info->glyphs + text_info->length, &shift, - drawing); + text_info->glyphs + text_info->length, drawing); + + // Add additional space after italic to non-italic style changes + if (text_info->length && + text_info->glyphs[text_info->length - 1].hash_key.italic && + !render_priv->state.italic) { + int back = text_info->length - 1; + GlyphInfo *og = &text_info->glyphs[back]; + while (back && og->bbox.xMax - og->bbox.xMin == 0 + && og->hash_key.italic) + og = &text_info->glyphs[--back]; + if (og->bbox.xMax > og->advance.x) { + // The FreeType oblique slants by 6/16 + pen.x += og->bbox.yMax * 0.375; + } + } text_info->glyphs[text_info->length].pos.x = pen.x; text_info->glyphs[text_info->length].pos.y = pen.y; pen.x += text_info->glyphs[text_info->length].advance.x; pen.x += double_to_d6(render_priv->state.hspacing * - render_priv->font_scale); + render_priv->font_scale + * render_priv->state.scale_x); pen.y += text_info->glyphs[text_info->length].advance.y; - pen.y += render_priv->state.fay * + pen.y += (render_priv->state.fay * render_priv->state.scale_y) * text_info->glyphs[text_info->length].advance.x; previous = code; @@ -2859,6 +2003,8 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event, 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.border_style = + render_priv->state.style->BorderStyle; text_info->glyphs[text_info->length].hash_key.shadow_offset.x = double_to_d6( render_priv->state.shadow_x * render_priv->border_scale - @@ -2869,6 +2015,8 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event, render_priv->state.shadow_y * render_priv->border_scale - (int) (render_priv->state.shadow_y * render_priv->border_scale)); + text_info->glyphs[text_info->length].hash_key.flags = + render_priv->state.flags; text_info->length++; @@ -2928,13 +2076,17 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event, if ((i == text_info->length) || text_info->glyphs[i].linebreak) { double width, shift = 0; - glyph_info_t *first_glyph = + GlyphInfo *first_glyph = text_info->glyphs + last_break + 1; - glyph_info_t *last_glyph = text_info->glyphs + i - 1; + GlyphInfo *last_glyph = text_info->glyphs + i - 1; + + while (first_glyph < last_glyph && first_glyph->skip) + first_glyph++; while ((last_glyph > first_glyph) && ((last_glyph->symbol == '\n') - || (last_glyph->symbol == 0))) + || (last_glyph->symbol == 0) + || (last_glyph->skip))) last_glyph--; width = d6_to_double( @@ -3062,7 +2214,7 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event, } // calculate rotation parameters { - double_vector_t center; + DVector center; if (render_priv->state.have_origin) { center.x = x2scr(render_priv, render_priv->state.org_x); @@ -3075,7 +2227,7 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event, } for (i = 0; i < text_info->length; ++i) { - glyph_info_t *info = text_info->glyphs + i; + GlyphInfo *info = text_info->glyphs + i; if (info->hash_key.frx || info->hash_key.fry || info->hash_key.frz || info->hash_key.fax @@ -3092,7 +2244,7 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event, // convert glyphs to bitmaps for (i = 0; i < text_info->length; ++i) { - glyph_info_t *g = text_info->glyphs + i; + GlyphInfo *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; @@ -3105,6 +2257,8 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event, 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->left = device_x + bbox.xMin + 0.5; + event_images->width = bbox.xMax - bbox.xMin + 0.5; event_images->detect_collisions = render_priv->state.detect_collisions; event_images->shift_direction = (valign == VALIGN_TOP) ? 1 : -1; event_images->event = event; @@ -3119,18 +2273,18 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event, * \brief deallocate image list * \param img list pointer */ -static void ass_free_images(ass_image_t *img) +static void ass_free_images(ASS_Image *img) { while (img) { - ass_image_t *next = img->next; + ASS_Image *next = img->next; free(img); img = next; } } -static void ass_reconfigure(ass_renderer_t *priv) +static void ass_reconfigure(ASS_Renderer *priv) { - priv->render_id = ++last_render_id; + priv->render_id++; priv->cache.glyph_cache = ass_glyph_cache_reset(priv->cache.glyph_cache); priv->cache.bitmap_cache = @@ -3141,20 +2295,20 @@ static void ass_reconfigure(ass_renderer_t *priv) 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 *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; - priv->settings.pixel_ratio = ((double) w) / h; + priv->settings.storage_aspect = ((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 *priv, int t, int b, int l, int r) { if (priv->settings.left_margin != l || priv->settings.right_margin != r || @@ -3168,21 +2322,21 @@ 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_use_margins(ASS_Renderer *priv, int use) { priv->settings.use_margins = use; } -void ass_set_aspect_ratio(ass_renderer_t *priv, double ar, double par) +void ass_set_aspect_ratio(ASS_Renderer *priv, double dar, double sar) { - if (priv->settings.aspect != ar || priv->settings.pixel_ratio != par) { - priv->settings.aspect = ar; - priv->settings.pixel_ratio = par; + if (priv->settings.aspect != dar || priv->settings.storage_aspect != sar) { + priv->settings.aspect = dar; + priv->settings.storage_aspect = sar; ass_reconfigure(priv); } } -void ass_set_font_scale(ass_renderer_t *priv, double font_scale) +void ass_set_font_scale(ASS_Renderer *priv, double font_scale) { if (priv->settings.font_size_coeff != font_scale) { priv->settings.font_size_coeff = font_scale; @@ -3190,7 +2344,7 @@ 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_hinting(ASS_Renderer *priv, ASS_Hinting ht) { if (priv->settings.hinting != ht) { priv->settings.hinting = ht; @@ -3198,20 +2352,15 @@ 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_line_spacing(ASS_Renderer *priv, double line_spacing) { priv->settings.line_spacing = line_spacing; } -void ass_set_fonts(ass_renderer_t *priv, const char *default_font, +void ass_set_fonts(ASS_Renderer *priv, const char *default_font, const char *default_family, int fc, const char *config, int update) { - if (priv->settings.default_font) - free(priv->settings.default_font); - if (priv->settings.default_family) - free(priv->settings.default_family); - free(priv->settings.default_font); free(priv->settings.default_family); priv->settings.default_font = default_font ? strdup(default_font) : 0; @@ -3225,7 +2374,7 @@ void ass_set_fonts(ass_renderer_t *priv, const char *default_font, default_font, fc, config, update); } -int ass_fonts_update(ass_renderer_t *render_priv) +int ass_fonts_update(ASS_Renderer *render_priv) { return fontconfig_update(render_priv->fontconfig_priv); } @@ -3234,11 +2383,11 @@ int ass_fonts_update(ass_renderer_t *render_priv) * \brief Start a new frame */ static int -ass_start_frame(ass_renderer_t *render_priv, ass_track_t *track, +ass_start_frame(ASS_Renderer *render_priv, ASS_Track *track, long long now) { - ass_settings_t *settings_priv = &render_priv->settings; - cache_store_t *cache = &render_priv->cache; + ASS_Settings *settings_priv = &render_priv->settings; + CacheStore *cache = &render_priv->cache; if (!render_priv->settings.frame_width && !render_priv->settings.frame_height) @@ -3284,7 +2433,7 @@ ass_start_frame(ass_renderer_t *render_priv, ass_track_t *track, // PAR correction render_priv->font_scale_x = render_priv->settings.aspect / - render_priv->settings.pixel_ratio; + render_priv->settings.storage_aspect; render_priv->prev_images_root = render_priv->images_root; render_priv->images_root = 0; @@ -3312,8 +2461,8 @@ ass_start_frame(ass_renderer_t *render_priv, ass_track_t *track, 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; + ASS_Event *e1 = ((EventImages *) p1)->event; + ASS_Event *e2 = ((EventImages *) p2)->event; if (e1->Layer < e2->Layer) return -1; if (e1->Layer > e2->Layer) @@ -3325,41 +2474,36 @@ static int cmp_event_layer(const void *p1, const void *p2) return 0; } -#define MAX_EVENTS 100 - -static render_priv_t *get_render_priv(ass_renderer_t *render_priv, - ass_event_t *event) +static ASS_RenderPriv *get_render_priv(ASS_Renderer *render_priv, + ASS_Event *event) { if (!event->render_priv) - event->render_priv = calloc(1, sizeof(render_priv_t)); - // FIXME: check render_id + event->render_priv = calloc(1, sizeof(ASS_RenderPriv)); if (render_priv->render_id != event->render_priv->render_id) { - memset(event->render_priv, 0, sizeof(render_priv_t)); + memset(event->render_priv, 0, sizeof(ASS_RenderPriv)); event->render_priv->render_id = render_priv->render_id; } + return event->render_priv; } -typedef struct segment_s { - int a, b; // top and height -} segment_t; - -static int overlap(segment_t *s1, segment_t *s2) +static int overlap(Segment *s1, Segment *s2) { - if (s1->a >= s2->b || s2->a >= s1->b) + if (s1->a >= s2->b || s2->a >= s1->b || + s1->ha >= s2->hb || s2->ha >= s1->hb) return 0; return 1; } static int cmp_segment(const void *p1, const void *p2) { - return ((segment_t *) p1)->a - ((segment_t *) p2)->a; + return ((Segment *) p1)->a - ((Segment *) p2)->a; } static void -shift_event(ass_renderer_t *render_priv, event_images_t *ei, int shift) +shift_event(ASS_Renderer *render_priv, EventImages *ei, int shift) { - ass_image_t *cur = ei->imgs; + ASS_Image *cur = ei->imgs; while (cur) { cur->dst_y += shift; // clip top and bottom @@ -3384,90 +2528,107 @@ shift_event(ass_renderer_t *render_priv, event_images_t *ei, int 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 *s, Segment *fixed, int *cnt, int dir) { 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) + if (s->b + shift <= fixed[i].a || s->a + shift >= fixed[i].b || + s->hb <= fixed[i].ha || s->ha >= fixed[i].hb) 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) + if (s->b + shift <= fixed[i].a || s->a + shift >= fixed[i].b || + s->hb <= fixed[i].ha || s->ha >= fixed[i].hb) continue; shift = fixed[i].a - s->b; } fixed[*cnt].a = s->a + shift; fixed[*cnt].b = s->b + shift; + fixed[*cnt].ha = s->ha; + fixed[*cnt].hb = s->hb; (*cnt)++; - qsort(fixed, *cnt, sizeof(segment_t), cmp_segment); + qsort(fixed, *cnt, sizeof(Segment), cmp_segment); return shift; } static void -fix_collisions(ass_renderer_t *render_priv, event_images_t *imgs, int cnt) +fix_collisions(ASS_Renderer *render_priv, EventImages *imgs, int cnt) { - segment_t used[MAX_EVENTS]; + Segment *used = malloc(cnt * sizeof(*used)); int cnt_used = 0; int i, j; // fill used[] with fixed events for (i = 0; i < cnt; ++i) { - render_priv_t *priv; + ASS_RenderPriv *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; + Segment s; s.a = priv->top; s.b = priv->top + priv->height; + s.ha = priv->left; + s.hb = priv->left + priv->width; if (priv->height != imgs[i].height) { // no, it's not ass_msg(render_priv->library, MSGL_WARN, "Warning! Event height has changed"); priv->top = 0; priv->height = 0; + priv->left = 0; + priv->width = 0; } for (j = 0; j < cnt_used; ++j) if (overlap(&s, used + j)) { // no, it's not priv->top = 0; priv->height = 0; + priv->left = 0; + priv->width = 0; } if (priv->height > 0) { // still a fixed event used[cnt_used].a = priv->top; used[cnt_used].b = priv->top + priv->height; + used[cnt_used].ha = priv->left; + used[cnt_used].hb = priv->left + priv->width; cnt_used++; shift_event(render_priv, imgs + i, priv->top - imgs[i].top); } } } - qsort(used, cnt_used, sizeof(segment_t), cmp_segment); + qsort(used, cnt_used, sizeof(Segment), cmp_segment); // try to fit other events in free spaces for (i = 0; i < cnt; ++i) { - render_priv_t *priv; + ASS_RenderPriv *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; + Segment 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); + s.ha = imgs[i].left; + s.hb = imgs[i].left + imgs[i].width; + 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; + priv->left = imgs[i].left; + priv->width = imgs[i].width; } } + + free(used); } /** @@ -3476,7 +2637,7 @@ fix_collisions(ass_renderer_t *render_priv, event_images_t *imgs, int cnt) * \param i2 second image * \return 0 if identical, 1 if different positions, 2 if different content */ -static int ass_image_compare(ass_image_t *i1, ass_image_t *i2) +static int ass_image_compare(ASS_Image *i1, ASS_Image *i2) { if (i1->w != i2->w) return 2; @@ -3500,16 +2661,16 @@ static int ass_image_compare(ass_image_t *i1, ass_image_t *i2) * \param priv library handle * \return 0 if identical, 1 if different positions, 2 if different content */ -static int ass_detect_change(ass_renderer_t *priv) +static int ass_detect_change(ASS_Renderer *priv) { - ass_image_t *img, *img2; + ASS_Image *img, *img2; int diff; img = priv->prev_images_root; img2 = priv->images_root; diff = 0; while (img && diff < 2) { - ass_image_t *next, *next2; + ASS_Image *next, *next2; next = img->next; if (img2) { int d = ass_image_compare(img, img2); @@ -3541,12 +2702,12 @@ 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 *ass_render_frame(ASS_Renderer *priv, ASS_Track *track, + long long now, int *detect_change) { int i, cnt, rc; - event_images_t *last; - ass_image_t **tail; + EventImages *last; + ASS_Image **tail; // init frame rc = ass_start_frame(priv, track, now); @@ -3556,14 +2717,14 @@ ass_image_t *ass_render_frame(ass_renderer_t *priv, ass_track_t *track, // render events separately cnt = 0; for (i = 0; i < track->n_events; ++i) { - ass_event_t *event = track->events + i; + ASS_Event *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)); + priv->eimg_size * sizeof(EventImages)); } rc = ass_render_event(priv, event, priv->eimg + cnt); if (!rc) @@ -3572,7 +2733,7 @@ ass_image_t *ass_render_frame(ass_renderer_t *priv, ass_track_t *track, } // sort by layer - qsort(priv->eimg, cnt, sizeof(event_images_t), cmp_event_layer); + qsort(priv->eimg, cnt, sizeof(EventImages), cmp_event_layer); // call fix_collisions for each group of events with the same layer last = priv->eimg; @@ -3587,7 +2748,7 @@ ass_image_t *ass_render_frame(ass_renderer_t *priv, ass_track_t *track, // concat lists tail = &priv->images_root; for (i = 0; i < cnt; ++i) { - ass_image_t *cur = priv->eimg[i].imgs; + ASS_Image *cur = priv->eimg[i].imgs; while (cur) { *tail = cur; tail = &cur->next; diff --git a/aegisub/libass/ass_render.h b/aegisub/libass/ass_render.h new file mode 100644 index 000000000..6d9db23fb --- /dev/null +++ b/aegisub/libass/ass_render.h @@ -0,0 +1,262 @@ +/* + * Copyright (C) 2006 Evgeniy Stepanov + * Copyright (C) 2009 Grigori Goronzy + * + * This file is part of libass. + * + * libass is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * libass is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with libass; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ + +#ifndef LIBASS_RENDER_H +#define LIBASS_RENDER_H + +#include +#include +#include FT_FREETYPE_H +#include FT_STROKER_H +#include FT_GLYPH_H +#include FT_SYNTHESIS_H + +#include "ass.h" +#include "ass_font.h" +#include "ass_bitmap.h" +#include "ass_cache.h" +#include "ass_utils.h" +#include "ass_fontconfig.h" +#include "ass_library.h" +#include "ass_drawing.h" + +typedef struct { + double xMin; + double xMax; + double yMin; + double yMax; +} DBBox; + +typedef struct { + double x; + double y; +} DVector; + +typedef struct free_list { + void *object; + struct free_list *next; +} FreeList; + +typedef struct { + 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 storage_aspect; // pixel ratio of the source image + ASS_Hinting hinting; + + char *default_font; + char *default_family; +} ASS_Settings; + +// a rendered event +typedef struct { + ASS_Image *imgs; + int top, height, left, width; + int detect_collisions; + int shift_direction; + ASS_Event *event; +} EventImages; + +typedef enum { + EF_NONE = 0, + EF_KARAOKE, + EF_KARAOKE_KF, + EF_KARAOKE_KO +} Effect; + +// describes a glyph +// GlyphInfo and TextInfo are used for text centering and word-wrapping operations +typedef struct { + unsigned symbol; + unsigned skip; // skip glyph when layouting text + FT_Glyph glyph; + FT_Glyph outline_glyph; + Bitmap *bm; // glyph bitmap + Bitmap *bm_o; // outline bitmap + Bitmap *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 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 be; // blur edges + double blur; // gaussian blur + double shadow_x; + double shadow_y; + double frx, fry, frz; // rotation + double fax, fay; // text shearing + + BitmapHashKey hash_key; +} GlyphInfo; + +typedef struct { + double asc, desc; +} LineInfo; + +typedef struct { + GlyphInfo *glyphs; + int length; + LineInfo *lines; + int n_lines; + double height; + int max_glyphs; + int max_lines; +} TextInfo; + +// Renderer state. +// Values like current font face, color, screen position, clipping and so on are stored here. +typedef struct { + ASS_Event *event; + ASS_Style *style; + + ASS_Font *font; + char *font_path; + double font_size; + int flags; // decoration flags (underline/strike-through) + + 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 *drawing; // current drawing + ASS_Drawing *clip_drawing; // clip vector + int clip_drawing_mode; // 0 = regular clip, 1 = inverse clip + + Effect 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; + int wrap_style; +} RenderContext; + +typedef struct { + Hashmap *font_cache; + Hashmap *glyph_cache; + Hashmap *bitmap_cache; + Hashmap *composite_cache; + size_t glyph_max; + size_t bitmap_max_size; +} CacheStore; + +struct ass_renderer { + ASS_Library *library; + FT_Library ftlibrary; + FCInstance *fontconfig_priv; + ASS_Settings settings; + int render_id; + ASS_SynthPriv *synth_priv; + + ASS_Image *images_root; // rendering result is stored here + ASS_Image *prev_images_root; + + EventImages *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 *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; + + RenderContext state; + TextInfo text_info; + CacheStore cache; + + FreeList *free_head; + FreeList *free_tail; +}; + +typedef struct render_priv { + int top, height, left, width; + int render_id; +} RenderPriv; + +typedef struct { + int x0; + int y0; + int x1; + int y1; +} Rect; + +typedef struct { + int a, b; // top and height + int ha, hb; // left and width +} Segment; + +void reset_render_context(ASS_Renderer *render_priv); + +#endif /* LIBASS_RENDER_H */ diff --git a/aegisub/libass/ass_types.h b/aegisub/libass/ass_types.h index fb74872e3..d6a011101 100644 --- a/aegisub/libass/ass_types.h +++ b/aegisub/libass/ass_types.h @@ -30,8 +30,14 @@ #define HALIGN_CENTER 2 #define HALIGN_RIGHT 3 +/* Opaque objects internally used by libass. Contents are private. */ +typedef struct ass_renderer ASS_Renderer; +typedef struct render_priv ASS_RenderPriv; +typedef struct parser_priv ASS_ParserPriv; +typedef struct ass_library ASS_Library; + /* ASS Style: line */ -typedef struct ass_style_s { +typedef struct ass_style { char *Name; char *FontName; double FontSize; @@ -56,15 +62,13 @@ typedef struct ass_style_s { int MarginV; int Encoding; int treat_fontname_as_pattern; -} ass_style_t; - -typedef struct render_priv_s render_priv_t; +} ASS_Style; /* - * ass_event_t corresponds to a single Dialogue line; + * ASS_Event corresponds to a single Dialogue line; * text is stored as-is, style overrides will be parsed later. */ -typedef struct ass_event_s { +typedef struct ass_event { long long Start; // ms long long Duration; // ms @@ -78,24 +82,21 @@ typedef struct ass_event_s { char *Effect; char *Text; - render_priv_t *render_priv; -} ass_event_t; - -typedef struct parser_priv_s parser_priv_t; -typedef struct ass_library_s ass_library_t; + ASS_RenderPriv *render_priv; +} ASS_Event; /* * 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 { +typedef struct ass_track { 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 + ASS_Style *styles; // array of styles, max_styles length, n_styles used + ASS_Event *events; // the same as styles char *style_format; // style format line (everything after "Format: ") char *event_format; // event format line @@ -111,14 +112,13 @@ typedef struct ass_track_s { int PlayResY; double Timer; int WrapStyle; - char ScaledBorderAndShadow; - + int ScaledBorderAndShadow; 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; + ASS_Library *library; + ASS_ParserPriv *parser_priv; +} ASS_Track; #endif /* LIBASS_TYPES_H */ diff --git a/aegisub/libass/ass_utils.c b/aegisub/libass/ass_utils.c index 56ca0ef63..6ca78b860 100644 --- a/aegisub/libass/ass_utils.c +++ b/aegisub/libass/ass_utils.c @@ -74,11 +74,12 @@ int mystrtod(char **p, double *res) return 0; } -int strtocolor(ass_library_t *library, char **q, uint32_t *res) +int strtocolor(ASS_Library *library, char **q, uint32_t *res, int hex) { uint32_t color = 0; int result; char *p = *q; + int base = hex ? 16 : 10; if (*p == '&') ++p; @@ -89,7 +90,7 @@ int strtocolor(ass_library_t *library, char **q, uint32_t *res) ++p; result = mystrtou32(&p, 16, &color); } else { - result = mystrtou32(&p, 0, &color); + result = mystrtou32(&p, base, &color); } { @@ -122,11 +123,11 @@ char parse_bool(char *str) return 0; } -void ass_msg(ass_library_t *priv, int lvl, char *fmt, ...) +void ass_msg(ASS_Library *priv, int lvl, char *fmt, ...) { va_list va; va_start(va, fmt); - priv->msg_callback(lvl, fmt, &va, priv->msg_callback_data); + priv->msg_callback(lvl, fmt, va, priv->msg_callback_data); va_end(va); } @@ -161,7 +162,7 @@ unsigned ass_utf8_get_char(char **str) } #ifdef CONFIG_ENCA -void *ass_guess_buffer_cp(ass_library_t *library, unsigned char *buffer, +void *ass_guess_buffer_cp(ASS_Library *library, unsigned char *buffer, int buflen, char *preferred_language, char *fallback) { diff --git a/aegisub/libass/ass_utils.h b/aegisub/libass/ass_utils.h index a906ca57a..bade57804 100644 --- a/aegisub/libass/ass_utils.h +++ b/aegisub/libass/ass_utils.h @@ -43,17 +43,18 @@ #define FFMAX(a,b) ((a) > (b) ? (a) : (b)) #define FFMIN(a,b) ((a) > (b) ? (b) : (a)) +#define FFMINMAX(c,a,b) FFMIN(FFMAX(c, a), b) 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(ass_library_t *library, char **q, uint32_t *res); +int strtocolor(ASS_Library *library, char **q, uint32_t *res, int hex); char parse_bool(char *str); unsigned ass_utf8_get_char(char **str); -void ass_msg(ass_library_t *priv, int lvl, char *fmt, ...); +void ass_msg(ASS_Library *priv, int lvl, char *fmt, ...); #ifdef CONFIG_ENCA -void *ass_guess_buffer_cp(ass_library_t *library, unsigned char *buffer, +void *ass_guess_buffer_cp(ASS_Library *library, unsigned char *buffer, int buflen, char *preferred_language, char *fallback); #endif diff --git a/aegisub/src/subtitles_provider_libass.cpp b/aegisub/src/subtitles_provider_libass.cpp index 3c6d6e4c7..057982732 100644 --- a/aegisub/src/subtitles_provider_libass.cpp +++ b/aegisub/src/subtitles_provider_libass.cpp @@ -56,7 +56,7 @@ extern "C" { #endif -/// @brief Constructor +/// @brief Constructor /// LibassSubtitlesProvider::LibassSubtitlesProvider() { // Initialize library @@ -64,12 +64,12 @@ LibassSubtitlesProvider::LibassSubtitlesProvider() { if (first) { ass_library = ass_library_init(); if (!ass_library) throw _T("ass_library_init failed"); - + wxString fonts_dir = StandardPaths::DecodePath(_T("?user/libass_fonts/")); if (!wxDirExists(fonts_dir)) // It's only one level below the user dir, and we assume the user dir already exists at this point. wxMkdir(fonts_dir); - + ass_set_fonts_dir(ass_library, fonts_dir.mb_str(wxConvFile)); ass_set_extract_fonts(ass_library, 0); ass_set_style_overrides(ass_library, NULL); @@ -98,15 +98,15 @@ LibassSubtitlesProvider::LibassSubtitlesProvider() { -/// @brief Destructor +/// @brief Destructor /// LibassSubtitlesProvider::~LibassSubtitlesProvider() { } -/// @brief Load subtitles -/// @param subs +/// @brief Load subtitles +/// @param subs /// void LibassSubtitlesProvider::LoadSubtitles(AssFile *subs) { // Prepare subtitles @@ -135,17 +135,17 @@ void LibassSubtitlesProvider::LoadSubtitles(AssFile *subs) { #define _a(c) ((c)&0xFF) -/// @brief Draw subtitles -/// @param frame -/// @param time -/// @return +/// @brief Draw subtitles +/// @param frame +/// @param time +/// @return /// void LibassSubtitlesProvider::DrawSubtitles(AegiVideoFrame &frame,double time) { // Set size ass_set_frame_size(ass_renderer, frame.w, frame.h); // Get frame - ass_image_t* img = ass_render_frame(ass_renderer, ass_track, int(time * 1000), NULL); + ASS_Image* img = ass_render_frame(ass_renderer, ass_track, int(time * 1000), NULL); // libass actually returns several alpha-masked monochrome images. // Here, we loop through their linked list, get the colour of the current, and blend into the frame. @@ -193,9 +193,7 @@ void LibassSubtitlesProvider::DrawSubtitles(AegiVideoFrame &frame,double time) { /// DOCME -ass_library_t* LibassSubtitlesProvider::ass_library; +ASS_Library* LibassSubtitlesProvider::ass_library; #endif // WITH_LIBASS - - diff --git a/aegisub/src/subtitles_provider_libass.h b/aegisub/src/subtitles_provider_libass.h index 81c341133..8c512de86 100644 --- a/aegisub/src/subtitles_provider_libass.h +++ b/aegisub/src/subtitles_provider_libass.h @@ -58,13 +58,13 @@ class LibassSubtitlesProvider : public SubtitlesProvider { private: /// DOCME - static ass_library_t* ass_library; + static ASS_Library* ass_library; /// DOCME - ass_renderer_t* ass_renderer; + ASS_Renderer* ass_renderer; /// DOCME - ass_track_t* ass_track; + ASS_Track* ass_track; public: LibassSubtitlesProvider(); @@ -91,5 +91,3 @@ public: }; #endif - -