From 7784a2610c728488e4f4dbb51235d2ecc31299f1 Mon Sep 17 00:00:00 2001 From: Grigori Goronzy Date: Sat, 5 Sep 2009 22:30:15 +0000 Subject: [PATCH] Merge libass updates into 2.1.8: r3176, r3446, r3448. Originally committed to SVN as r3491. --- aegisub/libass/Makefile.am | 3 +- aegisub/libass/ass.c | 134 +- aegisub/libass/ass.h | 297 +++- aegisub/libass/ass_bitmap.c | 48 +- aegisub/libass/ass_bitmap.h | 19 +- aegisub/libass/ass_cache.c | 130 +- aegisub/libass/ass_cache.h | 115 +- aegisub/libass/ass_cache_template.h | 28 +- aegisub/libass/ass_drawing.c | 78 +- aegisub/libass/ass_drawing.h | 34 +- aegisub/libass/ass_font.c | 132 +- aegisub/libass/ass_font.h | 34 +- aegisub/libass/ass_fontconfig.c | 81 +- aegisub/libass/ass_fontconfig.h | 15 +- aegisub/libass/ass_library.c | 25 +- aegisub/libass/ass_library.h | 10 +- aegisub/libass/ass_render.c | 1825 +++++++-------------- aegisub/libass/ass_types.h | 76 +- aegisub/libass/ass_utils.c | 11 +- aegisub/libass/ass_utils.h | 7 +- aegisub/src/subtitles_provider_libass.cpp | 10 +- aegisub/src/subtitles_provider_libass.h | 6 +- 22 files changed, 1384 insertions(+), 1734 deletions(-) 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 0a03ec2a0..908b199f2 100644 --- a/aegisub/libass/ass.h +++ b/aegisub/libass/ass.h @@ -25,138 +25,265 @@ #include #include "ass_types.h" -/// Libass renderer object. Contents are private. -typedef struct ass_renderer_s ass_renderer_t; +#define LIBASS_VERSION 0x00907010 -/// a linked list of images produced by ass renderer -typedef struct ass_image_s { - int w, h; // bitmap width/height - int stride; // bitmap stride +/* + * 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 - uint32_t color; // RGBA - int dst_x, dst_y; // bitmap placement inside the video frame + // 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; // linked list -} ass_image_t; + struct ass_image *next; // Next image, or NULL +} ASS_Image; -/// Hinting type -typedef enum { ASS_HINTING_NONE = 0, +/* + * 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 + * \brief Initialize the library. * \return library handle or NULL if failed */ -ass_library_t *ass_library_init(void); +ASS_Library *ass_library_init(void); /** - * \brief finalize the library + * \brief Finalize the library * \param priv library handle */ -void ass_library_done(ass_library_t *); +void ass_library_done(ASS_Library *priv); /** - * \brief set private font directory + * \brief Set private font directory. * It is used for saving embedded fonts and also in font lookup. + * + * \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); -void ass_set_extract_fonts(ass_library_t *priv, int extract); +/** + * \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 *priv, int extract); -void ass_set_style_overrides(ass_library_t *priv, char **list); +/** + * \brief Register style overrides with a library instance. + * The overrides should have the form [Style.]Param=Value, e.g. + * SomeStyle.Font=Arial + * ScaledBorderAndShadow=yes + * + * \param priv library handle + * \param list NULL-terminated list of strings + */ +void ass_set_style_overrides(ASS_Library *priv, char **list); -void ass_process_force_style(ass_track_t *track); +/** + * \brief Explicitly process style overrides for a track. + * \param track track handle + */ +void ass_process_force_style(ASS_Track *track); -void ass_set_message_cb(ass_library_t *priv, - void (*msg_cb)(int, char *, va_list *, void *), +/** + * \brief Register a callback for debug/info messages. + * If a callback is registered, it is called for every message emitted by + * libass. The callback receives a format string and a list of arguments, + * to be used for the printf family of functions. Additionally, a log level + * from 0 (FATAL errors) to 7 (verbose DEBUG) is passed. Usually, level 5 + * should be used by applications. + * If no callback is set, all messages level < 5 are printed to stderr, + * prefixed with [ass]. + * + * \param priv library handle + * \param msg_cb pointer to callback function + * \param data additional data, will be passed to callback + */ +void ass_set_message_cb(ASS_Library *priv, void (*msg_cb) + (int level, const char *fmt, va_list args, void *data), void *data); /** - * \brief initialize the renderer + * \brief Initialize the renderer. * \param priv library handle * \return renderer handle or NULL if failed */ -ass_renderer_t *ass_renderer_init(ass_library_t *); +ASS_Renderer *ass_renderer_init(ASS_Library *); /** - * \brief finalize the renderer + * \brief Finalize the renderer. * \param priv renderer handle */ -void ass_renderer_done(ass_renderer_t *priv); - -void ass_set_frame_size(ass_renderer_t *priv, int w, int h); -void ass_set_margins(ass_renderer_t *priv, int t, int b, int l, int r); -void ass_set_use_margins(ass_renderer_t *priv, int use); -void ass_set_aspect_ratio(ass_renderer_t *priv, double ar, double par); -void ass_set_font_scale(ass_renderer_t *priv, double font_scale); -void ass_set_hinting(ass_renderer_t *priv, ass_hinting_t ht); -void ass_set_line_spacing(ass_renderer_t *priv, double line_spacing); +void ass_renderer_done(ASS_Renderer *priv); /** - * \brief set font lookup defaults - * \param fc bool, use fontconfig? - * \param config path to fontconfig configuration file, or NULL. Only matters - * if fontconfig is used + * \brief Set the frame size in pixels, including margins. + * \param priv renderer handle + * \param w width + * \param h height */ -int ass_set_fonts(ass_renderer_t *priv, const char *default_font, - const char *default_family, int fc, const char *config); +void ass_set_frame_size(ASS_Renderer *priv, int w, int h); /** - * \brief render a frame, producing a list of ass_image_t - * \param priv library + * \brief Set frame margins. These values may be negative if pan-and-scan + * is used. + * \param priv renderer handle + * \param t top margin + * \param b bottom margin + * \param l left margin + * \param r right margin + */ +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 *priv, int use); + +/** + * \brief Set aspect ratio parameters. + * \param priv renderer handle + * \param dar display aspect ratio (DAR), prescaled for output PAR + * \param sar storage aspect ratio (SAR) + */ +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 *priv, double font_scale); + +/** + * \brief Set font hinting method. + * \param priv renderer handle + * \param ht hinting method + */ +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 *priv, double line_spacing); + +/** + * \brief Set font lookup defaults. + * \param fc whether to use fontconfig + * \param config path to fontconfig configuration file, or NULL. Only relevant + * if fontconfig is used. + * \param update whether fontconfig cache should be built/updated now. Only + * relevant if fontconfig is used. + */ +void ass_set_fonts(ASS_Renderer *priv, const char *default_font, + const char *default_family, int fc, const char *config, + int update); + +/** + * \brief Update/build font cache. This needs to be called if it was + * disabled when ass_set_fonts was set. + * + * \param priv renderer handle + * \return success + */ +int ass_fonts_update(ASS_Renderer *priv); + +/** + * \brief Set hard cache limits. Do not set, or set to zero, for reasonable + * defaults. + * + * \param priv renderer handle + * \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 *priv, int glyph_max, + int bitmap_max_size); + +/** + * \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); -// The following functions operate on track objects and do not need an ass_renderer // +/* + * The following functions operate on track objects and do not need + * an ass_renderer + */ /** - * \brief allocate a new empty track object + * \brief Allocate a new empty track object. + * \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) + * \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 + * \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 + * \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 + * \brief Delete a style. * \param track 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 + * \brief Delete an event. * \param track track * \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. @@ -164,73 +291,81 @@ 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 + * \brief Parse Codec Private section of subtitle stream. * \param track target track * \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, this contains exactly 1 event (or a commentary). + * \brief Parse a chunk of subtitle stream data. In Matroska, + * this contains exactly 1 event (or a commentary). * \param track track * \param data string to parse * \param size length of data * \param timecode starting time of the event (milliseconds) * \param duration duration of the event (milliseconds) -*/ -void ass_process_chunk(ass_track_t *track, char *data, int size, + */ +void ass_process_chunk(ASS_Track *track, char *data, int size, long long timecode, long long duration); /** * \brief Read subtitles from file. + * \param library library handle * \param fname file name + * \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. - * \param library libass library object + * \param library library handle * \param buf pointer to subtitles text * \param bufsize size of buffer - * \param codepage recode buffer contents from given codepage + * \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 + * \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. + * \param library library handle * \param name attachment name * \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 ass_library object + * \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 event, depending on movement parameter + * \brief Calculates timeshift from now to the start of some other subtitle + * event, depending on movement parameter. * \param track subtitle track - * \param now current time, ms + * \param now current time in milliseconds * \param movement how many events to skip from the one currently displayed * +2 means "the one after the next", -1 means "previous" - * \return timeshift, ms + * \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 */ +#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 4c7a3fa53..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,9 +220,15 @@ 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 if (val->bm) + bitmap_cache->cache_size += val->bm->w * val->bm->h * 3; + return hashmap_insert(bitmap_cache, key, val); } @@ -231,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); @@ -268,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) @@ -277,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); } @@ -288,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); @@ -324,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); } @@ -343,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 83c994386..5c9749f87 100644 --- a/aegisub/libass/ass_cache.h +++ b/aegisub/libass/ass_cache.h @@ -25,94 +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 f75bbd9ad..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) +static void drawing_finish(ASS_Drawing *drawing, int raw_mode) { int i, offset; FT_BBox bbox; @@ -127,6 +129,13 @@ static void drawing_finish(ass_drawing_t *drawing) printf("contour %d\n", ol->contours[i]); #endif + ass_msg(drawing->library, MSGL_V, + "Parsed drawing with %d points and %d contours", ol->n_points, + ol->n_contours); + + if (raw_mode) + return; + FT_Outline_Get_CBox(&drawing->glyph->outline, &bbox); drawing->glyph->root.advance.x = d6_to_d16(bbox.xMax - bbox.xMin); @@ -138,16 +147,12 @@ static void drawing_finish(ass_drawing_t *drawing) drawing->scale_y); for (i = 0; i < ol->n_points; i++) ol->points[i].y += offset; - - ass_msg(drawing->library, MSGL_V, - "Parsed drawing with %d points and %d contours", ol->n_points, - ol->n_contours); } /* * \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++) { @@ -159,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) { @@ -176,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; @@ -211,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; @@ -227,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; @@ -240,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); } @@ -253,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; @@ -264,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; @@ -355,13 +360,13 @@ 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 = malloc(DRAWING_INITIAL_SIZE); + drawing->text = calloc(1, DRAWING_INITIAL_SIZE); drawing->size = DRAWING_INITIAL_SIZE; drawing->ftlibrary = lib; @@ -379,8 +384,9 @@ 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); free(drawing); } @@ -388,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; @@ -403,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); } @@ -411,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) +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); @@ -474,9 +480,7 @@ FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing) } } - drawing_finish(drawing); + drawing_finish(drawing, raw_mode); drawing_free_tokens(drawing->tokens); return &drawing->glyph; } - - diff --git a/aegisub/libass/ass_drawing.h b/aegisub/libass/ass_drawing.h index dfd68f01b..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); +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 34ae292c4..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,15 +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)) { - *asc = face->size->metrics.ascender; - *desc = -face->size->metrics.descender; + int y_scale = face->size->metrics.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; } } @@ -284,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); @@ -308,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}, @@ -325,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}, @@ -347,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; @@ -434,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) { @@ -451,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; @@ -472,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 d8d64e229..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; @@ -427,14 +427,18 @@ static void process_fontdata(fc_instance_t *priv, ass_library_t *library, * \param ftlibrary freetype library object * \param family default font family * \param path default font path + * \param fc whether fontconfig should be used + * \param config path to a fontconfig configuration file, or NULL + * \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) +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; @@ -444,20 +448,23 @@ fc_instance_t *fontconfig_init(ass_library_t *library, goto exit; } - if (config) { - priv->config = FcConfigCreate(); - rc = FcConfigParseAndLoad(priv->config, (unsigned char *)config, - FcTrue); + 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); - FcConfigSetCurrent(priv->config); - } else { - rc = FcInit(); - assert(rc); - priv->config = FcConfigGetCurrent(); } 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; } @@ -507,44 +514,60 @@ fc_instance_t *fontconfig_init(ass_library_t *library, } priv->family_default = family ? strdup(family) : NULL; - exit: +exit: priv->path_default = path ? strdup(path) : NULL; priv->index_default = 0; return 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) +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(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 53441cce6..ad5b9f0e4 100644 --- a/aegisub/libass/ass_fontconfig.h +++ b/aegisub/libass/ass_fontconfig.h @@ -23,6 +23,7 @@ #include #include "ass_types.h" +#include "ass.h" #include #include FT_FREETYPE_H @@ -30,15 +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); -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); +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_render.c b/aegisub/libass/ass_render.c index ae65119af..edb0c840a 100644 --- a/aegisub/libass/ass_render.c +++ b/aegisub/libass/ass_render.c @@ -37,210 +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 -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 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 - - 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; -} 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; -}; - -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; @@ -250,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) { @@ -267,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); @@ -293,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; @@ -309,12 +114,14 @@ ass_renderer_t *ass_renderer_init(ass_library_t *library) priv->cache.bitmap_cache = ass_bitmap_cache_init(library); priv->cache.composite_cache = ass_composite_cache_init(library); priv->cache.glyph_cache = ass_glyph_cache_init(library); + priv->cache.glyph_max = GLYPH_CACHE_MAX; + priv->cache.bitmap_max_size = BITMAP_CACHE_MAX_SIZE; 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) @@ -325,12 +132,40 @@ ass_renderer_t *ass_renderer_init(ass_library_t *library) return priv; } -void ass_renderer_done(ass_renderer_t *render_priv) +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; + render_priv->cache.bitmap_max_size = bitmap_max ? 1048576 * bitmap_max : + BITMAP_CACHE_MAX_SIZE; +} + +static void free_list_clear(ASS_Renderer *render_priv) +{ + if (render_priv->free_head) { + FreeList *item = render_priv->free_head; + while(item) { + FreeList *oi = item; + free(item->object); + item = item->next; + free(oi); + } + render_priv->free_head = NULL; + } +} + +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; @@ -343,23 +178,25 @@ 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); free(render_priv->settings.default_font); 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; @@ -372,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) @@ -395,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; @@ -482,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 @@ -493,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) @@ -509,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; @@ -570,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; @@ -623,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; @@ -633,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; @@ -640,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; } @@ -663,27 +503,152 @@ 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 *render_priv, void *object) +{ + if (!render_priv->free_head) { + render_priv->free_head = calloc(1, sizeof(FreeList)); + render_priv->free_head->object = object; + render_priv->free_tail = render_priv->free_head; + } else { + FreeList *l = calloc(1, sizeof(FreeList)); + l->object = object; + render_priv->free_tail->next = l; + render_priv->free_tail = render_priv->free_tail->next; + } +} + /** - * \brief Convert text_info_t struct to ass_image_t list + * Iterate through a list of bitmaps and blend with clip vector, if + * 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 *render_priv, + ASS_Image *head) +{ + FT_Glyph glyph; + FT_BitmapGlyph clip_bm; + 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); + 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; + + assert(clip_bm->bitmap.pitch >= 0); + + // Iterate through bitmaps and blend/clip them + for (cur = head; cur; cur = cur->next) { + int left, top, right, bottom, apos, bpos, y, x, w, h; + int ax, ay, aw, ah, as; + int bx, by, bw, bh, bs; + int aleft, atop, bleft, btop; + unsigned char *abuffer, *bbuffer, *nbuffer; + + abuffer = cur->bitmap; + bbuffer = clip_bm->bitmap.buffer; + ax = cur->dst_x; + ay = cur->dst_y; + aw = cur->w; + ah = cur->h; + as = cur->stride; + bx = clip_bm->left; + by = clip_bm->top; + bw = clip_bm->bitmap.width; + bh = clip_bm->bitmap.rows; + bs = clip_bm->bitmap.pitch; + + // Calculate overlap coordinates + left = (ax > bx) ? ax : bx; + top = (ay > by) ? ay : by; + right = ((ax + aw) < (bx + bw)) ? (ax + aw) : (bx + bw); + bottom = ((ay + ah) < (by + bh)) ? (ay + ah) : (by + bh); + aleft = left - ax; + atop = top - ay; + w = right - left; + h = bottom - top; + bleft = left - bx; + btop = top - by; + + if (render_priv->state.clip_drawing_mode) { + // Inverse clip + if (ax + aw < bx || ay + ah < by || ax > bx + bw || + ay > by + bh) { + continue; + } + + // Allocate new buffer and add to free list + nbuffer = malloc(as * ah); + free_list_add(render_priv, nbuffer); + + // Blend together + 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; + bpos = (btop + y) * bs + bleft + x; + nbuffer[apos] = FFMAX(0, abuffer[apos] - bbuffer[bpos]); + } + } else { + // Regular clip + if (ax + aw < bx || ay + ah < by || ax > bx + bw || + ay > by + bh) { + cur->w = cur->h = 0; + continue; + } + + // Allocate new buffer and add to free list + nbuffer = calloc(as, ah); + free_list_add(render_priv, nbuffer); + + // Blend together + for (y = 0; y < h; y++) + for (x = 0; x < w; x++) { + apos = (atop + y) * as + aleft + x; + bpos = (btop + y) * bs + bleft + x; + nbuffer[apos] = (abuffer[apos] * bbuffer[bpos] + 255) >> 8; + } + } + cur->bitmap = nbuffer; + } + + // 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 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 = @@ -699,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); @@ -716,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; @@ -724,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); @@ -741,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); @@ -760,19 +726,21 @@ static ass_image_t *render_text(ass_renderer_t *render_priv, int dst_x, } *tail = 0; + blend_vector_clip(render_priv, head); + return head; } /** * \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; @@ -781,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 / @@ -806,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 / @@ -819,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; @@ -831,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); @@ -840,778 +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; -} - -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) -{ -#define skip_to(x) while ((*p != (x)) && (*p != '}') && (*p != 0)) { ++p;} -#define skip(x) if (*p == (x)) ++p; else { return p; } - - skip_to('\\'); - skip('\\'); - if ((*p == '}') || (*p == 0)) - return p; - - // New tags introduced in vsfilter 2.39 - if (mystrcmp(&p, "xbord")) { - double val; - if (mystrtod(&p, &val)) - val = render_priv->state.border_x * (1 - pwr) + val * pwr; - else - val = -1.; - change_border(render_priv, val, render_priv->state.border_y); - } else if (mystrcmp(&p, "ybord")) { - double val; - if (mystrtod(&p, &val)) - val = render_priv->state.border_y * (1 - pwr) + val * pwr; - else - val = -1.; - change_border(render_priv, render_priv->state.border_x, val); - } else if (mystrcmp(&p, "xshad")) { - double val; - if (mystrtod(&p, &val)) - val = render_priv->state.shadow_x * (1 - pwr) + val * pwr; - else - val = 0.; - render_priv->state.shadow_x = val; - } else if (mystrcmp(&p, "yshad")) { - double val; - if (mystrtod(&p, &val)) - val = render_priv->state.shadow_y * (1 - pwr) + val * pwr; - else - val = 0.; - render_priv->state.shadow_y = val; - } else if (mystrcmp(&p, "fax")) { - double val; - if (mystrtod(&p, &val)) - render_priv->state.fax = - val * pwr + render_priv->state.fax * (1 - pwr); - else - render_priv->state.fax = 0.; - } else if (mystrcmp(&p, "fay")) { - double val; - if (mystrtod(&p, &val)) - render_priv->state.fay = - val * pwr + render_priv->state.fay * (1 - pwr); - else - render_priv->state.fay = 0.; - } else if (mystrcmp(&p, "iclip")) { - int x0, y0, x1, y1; - int res = 1; - skip('('); - res &= mystrtoi(&p, &x0); - skip(','); - res &= mystrtoi(&p, &y0); - skip(','); - res &= mystrtoi(&p, &x1); - skip(','); - res &= mystrtoi(&p, &y1); - skip(')'); - if (res) { - render_priv->state.clip_x0 = - render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr; - render_priv->state.clip_x1 = - render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr; - render_priv->state.clip_y0 = - render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr; - render_priv->state.clip_y1 = - render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr; - render_priv->state.clip_mode = 1; - } else - render_priv->state.clip_mode = 0; - } else if (mystrcmp(&p, "blur")) { - double val; - if (mystrtod(&p, &val)) { - val = render_priv->state.blur * (1 - pwr) + val * pwr; - val = (val < 0) ? 0 : val; - val = (val > BLUR_MAX_RADIUS) ? BLUR_MAX_RADIUS : val; - render_priv->state.blur = val; - } else - render_priv->state.blur = 0.0; - // ASS standard tags - } else if (mystrcmp(&p, "fsc")) { - char tp = *p++; - double val; - if (tp == 'x') { - if (mystrtod(&p, &val)) { - val /= 100; - render_priv->state.scale_x = - render_priv->state.scale_x * (1 - pwr) + val * pwr; - } else - render_priv->state.scale_x = - render_priv->state.style->ScaleX; - } else if (tp == 'y') { - if (mystrtod(&p, &val)) { - val /= 100; - render_priv->state.scale_y = - render_priv->state.scale_y * (1 - pwr) + val * pwr; - } else - render_priv->state.scale_y = - render_priv->state.style->ScaleY; - } - } else if (mystrcmp(&p, "fsp")) { - double val; - if (mystrtod(&p, &val)) - render_priv->state.hspacing = - render_priv->state.hspacing * (1 - pwr) + val * pwr; - else - render_priv->state.hspacing = render_priv->state.style->Spacing; - } else if (mystrcmp(&p, "fs")) { - double val; - if (mystrtod(&p, &val)) - val = render_priv->state.font_size * (1 - pwr) + val * pwr; - else - val = render_priv->state.style->FontSize; - if (render_priv->state.font) - change_font_size(render_priv, val); - } else if (mystrcmp(&p, "bord")) { - double val; - if (mystrtod(&p, &val)) { - if (render_priv->state.border_x == render_priv->state.border_y) - val = render_priv->state.border_x * (1 - pwr) + val * pwr; - } else - val = -1.; // reset to default - change_border(render_priv, val, val); - } else if (mystrcmp(&p, "move")) { - double x1, x2, y1, y2; - long long t1, t2, delta_t, t; - double x, y; - double k; - skip('('); - mystrtod(&p, &x1); - skip(','); - mystrtod(&p, &y1); - skip(','); - mystrtod(&p, &x2); - skip(','); - mystrtod(&p, &y2); - if (*p == ',') { - skip(','); - mystrtoll(&p, &t1); - skip(','); - mystrtoll(&p, &t2); - ass_msg(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")) { - int x0, y0, x1, y1; - int res = 1; - skip('('); - res &= mystrtoi(&p, &x0); - skip(','); - res &= mystrtoi(&p, &y0); - skip(','); - res &= mystrtoi(&p, &x1); - skip(','); - res &= mystrtoi(&p, &y1); - skip(')'); - if (res) { - render_priv->state.clip_x0 = - render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr; - render_priv->state.clip_x1 = - render_priv->state.clip_x1 * (1 - pwr) + x1 * pwr; - render_priv->state.clip_y0 = - render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr; - render_priv->state.clip_y1 = - render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr; - } else { - render_priv->state.clip_x0 = 0; - render_priv->state.clip_y0 = 0; - render_priv->state.clip_x1 = render_priv->track->PlayResX; - render_priv->state.clip_y1 = render_priv->track->PlayResY; - } - } else if (mystrcmp(&p, "c")) { - uint32_t val; - if (!strtocolor(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 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; @@ -1690,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; @@ -1701,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; @@ -1721,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. } @@ -1729,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; @@ -1762,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 @@ -1849,14 +1053,72 @@ 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) + if (sx <= 0 && sy <= 0) return; fix_freetype_stroker(*glyph, sx, sy); @@ -1896,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; @@ -1926,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) { @@ -1946,9 +1206,9 @@ 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); + ass_drawing_parse(drawing, 0); FT_Glyph_Copy((FT_Glyph) drawing->glyph, &info->glyph); } else { info->glyph = @@ -1963,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, @@ -1991,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 @@ -2002,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); @@ -2015,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) @@ -2046,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; @@ -2073,19 +1349,20 @@ 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.; + GlyphInfo *last = NULL; int i; int empty_line = 1; text_info->height = 0.; for (i = 0; i < text_info->length + 1; ++i) { if ((i == text_info->length) || text_info->glyphs[i].linebreak) { - if (empty_line && cur_line > 0) { - max_asc = text_info->lines[cur_line - 1].asc / 2.0; - max_desc = text_info->lines[cur_line - 1].desc / 2.0; + if (empty_line && cur_line > 0 && last && i < text_info->length) { + max_asc = d6_to_double(last->asc) / 2.0; + max_desc = d6_to_double(last->desc) / 2.0; } text_info->lines[cur_line].asc = max_asc; text_info->lines[cur_line].desc = max_desc; @@ -2096,11 +1373,13 @@ 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) max_desc = d6_to_double(cur->desc); + if (cur->symbol != '\n' && cur->symbol != 0) + last = cur; } } text_info->height += @@ -2108,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 @@ -2116,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; @@ -2150,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) @@ -2170,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) @@ -2191,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; @@ -2245,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; @@ -2278,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 @@ -2343,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; @@ -2379,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); @@ -2396,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.; @@ -2434,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); } } @@ -2454,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"); @@ -2530,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 @@ -2539,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; @@ -2641,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 - @@ -2651,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++; @@ -2668,6 +2034,7 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event, } } + if (text_info->length == 0) { // no valid symbols in the event; this can be smth like {comment} free_render_context(render_priv); @@ -2709,17 +2076,21 @@ 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( - last_glyph->pos.x + last_glyph->advance.x - + last_glyph->pos.x + last_glyph->advance.x - first_glyph->pos.x); if (halign == HALIGN_LEFT) { // left aligned, no action shift = 0; @@ -2769,7 +2140,7 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event, } else if (valign == VALIGN_CENTER) { // midtitle double scr_y = y2scr(render_priv, render_priv->track->PlayResY / 2.0); - device_y = scr_y - (bbox.yMax - bbox.yMin) / 2.0; + device_y = scr_y - (bbox.yMax + bbox.yMin) / 2.0; } else { // subtitle double scr_y; if (valign != VALIGN_SUB) @@ -2843,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); @@ -2856,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 @@ -2873,11 +2244,11 @@ 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; - g->hash_key.advance.y = + g->hash_key.advance.y = double_to_d6(device_y - (int) device_y + d6_to_double(g->pos.y & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY; get_bitmap_glyph(render_priv, text_info->glyphs + i); @@ -2886,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; @@ -2900,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 = @@ -2922,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 || @@ -2949,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; @@ -2971,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; @@ -2979,19 +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; } -int ass_set_fonts(ass_renderer_t *priv, const char *default_font, - const char *default_family, int fc, const char *config) +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; @@ -3002,27 +2371,33 @@ int ass_set_fonts(ass_renderer_t *priv, const char *default_font, fontconfig_done(priv->fontconfig_priv); priv->fontconfig_priv = fontconfig_init(priv->library, priv->ftlibrary, default_family, - default_font, fc, config); + default_font, fc, config, update); +} - return !!priv->fontconfig_priv; +int ass_fonts_update(ASS_Renderer *render_priv) +{ + return fontconfig_update(render_priv->fontconfig_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) { - if (render_priv->library != track->library) - return 1; - - ass_settings_t *settings_priv = &render_priv->settings; + ASS_Settings *settings_priv = &render_priv->settings; + CacheStore *cache = &render_priv->cache; if (!render_priv->settings.frame_width && !render_priv->settings.frame_height) return 1; // library not initialized + if (render_priv->library != track->library) + return 1; + + free_list_clear(render_priv); + if (track->n_events == 0) return 1; // nothing to do @@ -3058,18 +2433,36 @@ 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; + if (cache->bitmap_cache->cache_size > cache->bitmap_max_size) { + ass_msg(render_priv->library, MSGL_V, + "Hitting hard bitmap cache limit (was: %ld bytes), " + "resetting.", (long) cache->bitmap_cache->cache_size); + cache->bitmap_cache = ass_bitmap_cache_reset(cache->bitmap_cache); + cache->composite_cache = ass_composite_cache_reset( + cache->composite_cache); + ass_free_images(render_priv->prev_images_root); + render_priv->prev_images_root = 0; + } + + if (cache->glyph_cache->count > cache->glyph_max) { + ass_msg(render_priv->library, MSGL_V, + "Hitting hard glyph cache limit (was: %ld glyphs), resetting.", + (long) cache->glyph_cache->count); + cache->glyph_cache = ass_glyph_cache_reset(cache->glyph_cache); + } + return 0; } 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) @@ -3081,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 @@ -3140,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); } /** @@ -3232,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; @@ -3256,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); @@ -3297,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); @@ -3312,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) @@ -3328,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; @@ -3343,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_types.h b/aegisub/libass/ass_types.h index cdea14ef4..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 -/// ass Style: line -typedef struct ass_style_s { +/* 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 { char *Name; char *FontName; double FontSize; @@ -54,16 +60,15 @@ typedef struct ass_style_s { int MarginL; int MarginR; int MarginV; -// int AlphaLevel; int Encoding; int treat_fontname_as_pattern; -} ass_style_t; +} ASS_Style; -typedef struct render_priv_s render_priv_t; - -/// ass_event_t corresponds to a single Dialogue line -/// Text is stored as-is, style overrides will be parsed later -typedef struct ass_event_s { +/* + * ASS_Event corresponds to a single Dialogue line; + * text is stored as-is, style overrides will be parsed later. + */ +typedef struct ass_event { long long Start; // ms long long Duration; // ms @@ -77,42 +82,43 @@ typedef struct ass_event_s { char *Effect; char *Text; - render_priv_t *render_priv; -} ass_event_t; + ASS_RenderPriv *render_priv; +} ASS_Event; -typedef struct parser_priv_s parser_priv_t; - -typedef struct ass_library_s ass_library_t; - -/// ass track represent either an external script or a matroska subtitle stream (no real difference between them) -/// it can be used in rendering after the headers are parsed (i.e. events format line read) -typedef struct ass_track_s { - int n_styles; // amount used - int max_styles; // amount allocated +/* + * 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 { + 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 + char *style_format; // style format line (everything after "Format: ") + char *event_format; // event format line - enum { TRACK_TYPE_UNKNOWN = - 0, TRACK_TYPE_ASS, TRACK_TYPE_SSA } track_type; + enum { + TRACK_TYPE_UNKNOWN = 0, + TRACK_TYPE_ASS, + TRACK_TYPE_SSA + } track_type; - // script header fields + // Script header fields int PlayResX; 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 - int default_style; // index of default style - char *name; // file name in case of external subs, 0 for streams + ASS_Library *library; + ASS_ParserPriv *parser_priv; +} ASS_Track; - ass_library_t *library; - parser_priv_t *parser_priv; -} ass_track_t; - -#endif /* LIBASS_TYPES_H */ +#endif /* LIBASS_TYPES_H */ diff --git a/aegisub/libass/ass_utils.c b/aegisub/libass/ass_utils.c index 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 1c1b53d87..69b7e465a 100644 --- a/aegisub/src/subtitles_provider_libass.cpp +++ b/aegisub/src/subtitles_provider_libass.cpp @@ -62,12 +62,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); @@ -91,7 +91,7 @@ LibassSubtitlesProvider::LibassSubtitlesProvider() { const char *config_path = NULL; #endif - ass_set_fonts(ass_renderer, NULL, "Sans", 1, config_path); + ass_set_fonts(ass_renderer, NULL, "Sans", 1, config_path, 1); } @@ -130,7 +130,7 @@ void LibassSubtitlesProvider::DrawSubtitles(AegiVideoFrame &frame,double time) { 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. @@ -178,7 +178,7 @@ void LibassSubtitlesProvider::DrawSubtitles(AegiVideoFrame &frame,double time) { ////////// // Static -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 988e39ed7..6e233ab48 100644 --- a/aegisub/src/subtitles_provider_libass.h +++ b/aegisub/src/subtitles_provider_libass.h @@ -51,9 +51,9 @@ extern "C" { // libass provider class LibassSubtitlesProvider : public SubtitlesProvider { private: - static ass_library_t* ass_library; - ass_renderer_t* ass_renderer; - ass_track_t* ass_track; + static ASS_Library* ass_library; + ASS_Renderer* ass_renderer; + ASS_Track* ass_track; public: LibassSubtitlesProvider();