Update to 9ad13b from the libass git repo. (http://greg.geekmind.org/viewgit/) This change also includes the ability to split off fontconfig cache updating into it's own step so dialogues can be used to warn the user about the font cache being updated (it can take several mins on slow machines with lots of fonts). Updates #841

Originally committed to SVN as r3176.
This commit is contained in:
Amar Takhar 2009-07-19 19:33:29 +00:00
parent ee537d06f6
commit c329c4e047
11 changed files with 512 additions and 114 deletions

View file

@ -25,125 +25,239 @@
#include <stdarg.h> #include <stdarg.h>
#include "ass_types.h" #include "ass_types.h"
/// Libass renderer object. Contents are private. #define LIBASS_VERSION 0x00907000
/* Libass renderer object. Contents are private. */
typedef struct ass_renderer_s ass_renderer_t; typedef struct ass_renderer_s ass_renderer_t;
/// a linked list of images produced by ass renderer /* A linked list of images produced by ass renderer. */
typedef struct ass_image_s { typedef struct ass_image_s {
int w, h; // bitmap width/height int w, h; // Bitmap width/height
int stride; // bitmap stride int stride; // Bitmap stride
unsigned char *bitmap; // 1bpp stride*h alpha buffer unsigned char *bitmap; // 1bpp stride*h alpha buffer
uint32_t color; // RGBA uint32_t color; // Bitmap color and alpha, RGBA
int dst_x, dst_y; // bitmap placement inside the video frame int dst_x, dst_y; // Bitmap placement inside the video frame
struct ass_image_s *next; // linked list struct ass_image_s *next; // Next image, or NULL
} ass_image_t; } ass_image_t;
/// Hinting type /* Hinting type. */
typedef enum { ASS_HINTING_NONE = 0, typedef enum {
ASS_HINTING_NONE = 0,
ASS_HINTING_LIGHT, ASS_HINTING_LIGHT,
ASS_HINTING_NORMAL, ASS_HINTING_NORMAL,
ASS_HINTING_NATIVE ASS_HINTING_NATIVE
} ass_hinting_t; } ass_hinting_t;
/** /**
* \brief initialize the library * \brief Initialize the library.
* \return library handle or NULL if failed * \return library handle or NULL if failed
*/ */
ass_library_t *ass_library_init(void); ass_library_t *ass_library_init(void);
/** /**
* \brief finalize the library * \brief Finalize the library
* \param priv library handle * \param priv library handle
*/ */
void ass_library_done(ass_library_t *); void ass_library_done(ass_library_t *);
/** /**
* \brief set private font directory * \brief Set private font directory.
* It is used for saving embedded fonts and also in font lookup. * 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_t *priv, const char *fonts_dir);
/**
* \brief Whether fonts should be extracted from track data.
* \param priv library handle
* \param extract whether to extract fonts
*/
void ass_set_extract_fonts(ass_library_t *priv, int extract); void ass_set_extract_fonts(ass_library_t *priv, int extract);
/**
* \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_t *priv, char **list); void ass_set_style_overrides(ass_library_t *priv, char **list);
/**
* \brief Explicitly process style overrides for a track.
* \param track track handle
*/
void ass_process_force_style(ass_track_t *track); void ass_process_force_style(ass_track_t *track);
/**
* \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_t *priv, void ass_set_message_cb(ass_library_t *priv,
void (*msg_cb)(int, char *, va_list *, void *), void (*msg_cb)(int, char *, va_list *, void *),
void *data); void *data);
/** /**
* \brief initialize the renderer * \brief Initialize the renderer.
* \param priv library handle * \param priv library handle
* \return renderer handle or NULL if failed * \return renderer handle or NULL if failed
*/ */
ass_renderer_t *ass_renderer_init(ass_library_t *); ass_renderer_t *ass_renderer_init(ass_library_t *);
/** /**
* \brief finalize the renderer * \brief Finalize the renderer.
* \param priv renderer handle * \param priv renderer handle
*/ */
void ass_renderer_done(ass_renderer_t *priv); void ass_renderer_done(ass_renderer_t *priv);
/**
* \brief Set the frame size in pixels, including margins.
* \param priv renderer handle
* \param w width
* \param h height
*/
void ass_set_frame_size(ass_renderer_t *priv, int w, int h); void ass_set_frame_size(ass_renderer_t *priv, int w, int h);
/**
* \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_t *priv, int t, int b, int l, int r); void ass_set_margins(ass_renderer_t *priv, int t, int b, int l, int r);
/**
* \brief Whether margins should be used for placing regular events.
* \param priv renderer handle
* \param use whether to use the margins
*/
void ass_set_use_margins(ass_renderer_t *priv, int use); void ass_set_use_margins(ass_renderer_t *priv, int use);
/**
* \brief Set aspect ratio parameters.
* \param priv renderer handle
* \param ar physical aspect ratio
* \param par pixel ratio, e.g. width / height of the video
*/
void ass_set_aspect_ratio(ass_renderer_t *priv, double ar, double par); void ass_set_aspect_ratio(ass_renderer_t *priv, double ar, double par);
/**
* \brief Set a fixed font scaling factor.
* \param priv renderer handle
* \param font_scale scaling factor, default is 1.0
*/
void ass_set_font_scale(ass_renderer_t *priv, double font_scale); void ass_set_font_scale(ass_renderer_t *priv, double font_scale);
/**
* \brief Set font hinting method.
* \param priv renderer handle
* \param ht hinting method
*/
void ass_set_hinting(ass_renderer_t *priv, ass_hinting_t ht); void ass_set_hinting(ass_renderer_t *priv, ass_hinting_t ht);
/**
* \brief Set line spacing. Will not be scaled with frame size.
* \param priv renderer handle
* \param line_spacing line spacing in pixels
*/
void ass_set_line_spacing(ass_renderer_t *priv, double line_spacing); void ass_set_line_spacing(ass_renderer_t *priv, double line_spacing);
/** /**
* \brief set font lookup defaults * \brief Set font lookup defaults.
* \param fc bool, use fontconfig? * \param fc whether to use fontconfig
* \param config path to fontconfig configuration file, or NULL. Only matters * \param config path to fontconfig configuration file, or NULL. Only relevant
* if fontconfig is used * if fontconfig is used.
* \param update whether fontconfig cache should be built/updated now. Only
* relevant if fontconfig is used.
*/ */
int ass_set_fonts(ass_renderer_t *priv, const char *default_font, void ass_set_fonts(ass_renderer_t *priv, const char *default_font,
const char *default_family, int fc, const char *config); const char *default_family, int fc, const char *config,
int update);
/** /**
* \brief render a frame, producing a list of ass_image_t * \brief Update/build font cache. This needs to be called if it was
* \param priv library * disabled when ass_set_fonts was set.
*
* \param priv renderer handle
* \return success
*/
int ass_fonts_update(ass_renderer_t *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_t *priv, int glyph_max,
int bitmap_max_size);
/**
* \brief Render a frame, producing a list of ass_image_t.
* \param priv renderer handle
* \param track subtitle track * \param track subtitle track
* \param now video timestamp in milliseconds * \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, ass_image_t *ass_render_frame(ass_renderer_t *priv, ass_track_t *track,
long long now, int *detect_change); 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 * \return pointer to empty track
*/ */
ass_track_t *ass_new_track(ass_library_t *); ass_track_t *ass_new_track(ass_library_t *);
/** /**
* \brief deallocate track and all its child objects (styles and events) * \brief Deallocate track and all its child objects (styles and events).
* \param track track to deallocate * \param track track to deallocate
*/ */
void ass_free_track(ass_track_t *track); void ass_free_track(ass_track_t *track);
/** /**
* \brief allocate new style * \brief Allocate new style.
* \param track track * \param track track
* \return newly allocated style id * \return newly allocated style id
*/ */
int ass_alloc_style(ass_track_t *track); int ass_alloc_style(ass_track_t *track);
/** /**
* \brief allocate new event * \brief Allocate new event.
* \param track track * \param track track
* \return newly allocated event id * \return newly allocated event id
*/ */
int ass_alloc_event(ass_track_t *track); int ass_alloc_event(ass_track_t *track);
/** /**
* \brief delete a style * \brief Delete a style.
* \param track track * \param track track
* \param sid style id * \param sid style id
* Deallocates style data. Does not modify track->n_styles. * Deallocates style data. Does not modify track->n_styles.
@ -151,7 +265,7 @@ int ass_alloc_event(ass_track_t *track);
void ass_free_style(ass_track_t *track, int sid); void ass_free_style(ass_track_t *track, int sid);
/** /**
* \brief delete an event * \brief Delete an event.
* \param track track * \param track track
* \param eid event id * \param eid event id
* Deallocates event data. Does not modify track->n_events. * Deallocates event data. Does not modify track->n_events.
@ -167,7 +281,7 @@ void ass_free_event(ass_track_t *track, int eid);
void ass_process_data(ass_track_t *track, char *data, int size); void ass_process_data(ass_track_t *track, char *data, int size);
/** /**
* \brief Parse Codec Private section of subtitle stream * \brief Parse Codec Private section of subtitle stream.
* \param track target track * \param track target track
* \param data string to parse * \param data string to parse
* \param size length of data * \param size length of data
@ -175,19 +289,22 @@ void ass_process_data(ass_track_t *track, char *data, int size);
void ass_process_codec_private(ass_track_t *track, char *data, int size); void ass_process_codec_private(ass_track_t *track, char *data, int size);
/** /**
* \brief Parse a chunk of subtitle stream data. In Matroska, this contains exactly 1 event (or a commentary). * \brief Parse a chunk of subtitle stream data. In Matroska,
* this contains exactly 1 event (or a commentary).
* \param track track * \param track track
* \param data string to parse * \param data string to parse
* \param size length of data * \param size length of data
* \param timecode starting time of the event (milliseconds) * \param timecode starting time of the event (milliseconds)
* \param duration duration 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_t *track, char *data, int size,
long long timecode, long long duration); long long timecode, long long duration);
/** /**
* \brief Read subtitles from file. * \brief Read subtitles from file.
* \param library library handle
* \param fname file name * \param fname file name
* \param codepage encoding (iconv format)
* \return newly allocated track * \return newly allocated track
*/ */
ass_track_t *ass_read_file(ass_library_t *library, char *fname, ass_track_t *ass_read_file(ass_library_t *library, char *fname,
@ -195,22 +312,25 @@ ass_track_t *ass_read_file(ass_library_t *library, char *fname,
/** /**
* \brief Read subtitles from memory. * \brief Read subtitles from memory.
* \param library libass library object * \param library library handle
* \param buf pointer to subtitles text * \param buf pointer to subtitles text
* \param bufsize size of buffer * \param bufsize size of buffer
* \param codepage recode buffer contents from given codepage * \param codepage encoding (iconv format)
* \return newly allocated track * \return newly allocated track
*/ */
ass_track_t *ass_read_memory(ass_library_t *library, char *buf, ass_track_t *ass_read_memory(ass_library_t *library, char *buf,
size_t bufsize, char *codepage); 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 * \return 0 on success
*/ */
int ass_read_styles(ass_track_t *track, char *fname, char *codepage); int ass_read_styles(ass_track_t *track, char *fname, char *codepage);
/** /**
* \brief Add a memory font. * \brief Add a memory font.
* \param library library handle
* \param name attachment name * \param name attachment name
* \param data binary font data * \param data binary font data
* \param data_size data size * \param data_size data size
@ -219,18 +339,20 @@ void ass_add_font(ass_library_t *library, char *name, char *data,
int data_size); 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_t *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 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 * \param movement how many events to skip from the one currently displayed
* +2 means "the one after the next", -1 means "previous" * +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_t *track, long long now, int movement);
#endif /* LIBASS_ASS_H */ #endif /* LIBASS_ASS_H */

View file

@ -223,6 +223,12 @@ static void bitmap_hash_dtor(void *key, size_t key_size, void *value,
void *cache_add_bitmap(hashmap_t *bitmap_cache, bitmap_hash_key_t *key, void *cache_add_bitmap(hashmap_t *bitmap_cache, bitmap_hash_key_t *key,
bitmap_hash_val_t *val) bitmap_hash_val_t *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
bitmap_cache->cache_size += val->bm->w * val->bm->h * 3;
return hashmap_insert(bitmap_cache, key, val); return hashmap_insert(bitmap_cache, key, val);
} }

View file

@ -45,6 +45,7 @@ typedef struct hashmap_s {
hashmap_item_dtor_t item_dtor; // a destructor for hashmap key/value pairs hashmap_item_dtor_t item_dtor; // a destructor for hashmap key/value pairs
hashmap_key_compare_t key_compare; hashmap_key_compare_t key_compare;
hashmap_hash_t hash; hashmap_hash_t hash;
size_t cache_size;
// stats // stats
int hit_count; int hit_count;
int miss_count; int miss_count;

View file

@ -106,7 +106,7 @@ static void drawing_prepare(ass_drawing_t *drawing)
* \brief Finish a drawing. This only sets the horizontal advance according * \brief Finish a drawing. This only sets the horizontal advance according
* to the glyph's bbox at the moment. * to the glyph's bbox at the moment.
*/ */
static void drawing_finish(ass_drawing_t *drawing) static void drawing_finish(ass_drawing_t *drawing, int raw_mode)
{ {
int i, offset; int i, offset;
FT_BBox bbox; FT_BBox bbox;
@ -127,6 +127,13 @@ static void drawing_finish(ass_drawing_t *drawing)
printf("contour %d\n", ol->contours[i]); printf("contour %d\n", ol->contours[i]);
#endif #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); FT_Outline_Get_CBox(&drawing->glyph->outline, &bbox);
drawing->glyph->root.advance.x = d6_to_d16(bbox.xMax - bbox.xMin); drawing->glyph->root.advance.x = d6_to_d16(bbox.xMax - bbox.xMin);
@ -138,10 +145,6 @@ static void drawing_finish(ass_drawing_t *drawing)
drawing->scale_y); drawing->scale_y);
for (i = 0; i < ol->n_points; i++) for (i = 0; i < ol->n_points; i++)
ol->points[i].y += offset; 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);
} }
/* /*
@ -361,7 +364,7 @@ ass_drawing_t *ass_drawing_new(void *fontconfig_priv, ass_font_t *font,
ass_drawing_t* drawing; ass_drawing_t* drawing;
drawing = calloc(1, sizeof(*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->size = DRAWING_INITIAL_SIZE;
drawing->ftlibrary = lib; drawing->ftlibrary = lib;
@ -381,6 +384,7 @@ ass_drawing_t *ass_drawing_new(void *fontconfig_priv, ass_font_t *font,
*/ */
void ass_drawing_free(ass_drawing_t* drawing) void ass_drawing_free(ass_drawing_t* drawing)
{ {
FT_Done_Glyph((FT_Glyph) drawing->glyph);
free(drawing->text); free(drawing->text);
free(drawing); free(drawing);
} }
@ -411,7 +415,7 @@ void ass_drawing_hash(ass_drawing_t* drawing)
/* /*
* \brief Convert token list to outline. Calls the line and curve evaluators. * \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_t *drawing, int raw_mode)
{ {
int started = 0; int started = 0;
ass_drawing_token_t *token; ass_drawing_token_t *token;
@ -474,9 +478,7 @@ FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing)
} }
} }
drawing_finish(drawing); drawing_finish(drawing, raw_mode);
drawing_free_tokens(drawing->tokens); drawing_free_tokens(drawing->tokens);
return &drawing->glyph; return &drawing->glyph;
} }

View file

@ -72,6 +72,6 @@ ass_drawing_t *ass_drawing_new(void *fontconfig_priv, ass_font_t *font,
void ass_drawing_free(ass_drawing_t* drawing); void ass_drawing_free(ass_drawing_t* drawing);
void ass_drawing_add_char(ass_drawing_t* drawing, char symbol); void ass_drawing_add_char(ass_drawing_t* drawing, char symbol);
void ass_drawing_hash(ass_drawing_t* drawing); void ass_drawing_hash(ass_drawing_t* drawing);
FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing); FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing, int raw_mode);
#endif /* LIBASS_DRAWING_H */ #endif /* LIBASS_DRAWING_H */

View file

@ -268,8 +268,9 @@ void ass_font_get_asc_desc(ass_font_t *font, uint32_t ch, int *asc,
for (i = 0; i < font->n_faces; ++i) { for (i = 0; i < font->n_faces; ++i) {
FT_Face face = font->faces[i]; FT_Face face = font->faces[i];
if (FT_Get_Char_Index(face, ch)) { if (FT_Get_Char_Index(face, ch)) {
*asc = face->size->metrics.ascender; int y_scale = face->size->metrics.y_scale;
*desc = -face->size->metrics.descender; *asc = FT_MulFix(face->ascender, y_scale);
*desc = FT_MulFix(-face->descender, y_scale);
return; return;
} }
} }

View file

@ -427,11 +427,15 @@ static void process_fontdata(fc_instance_t *priv, ass_library_t *library,
* \param ftlibrary freetype library object * \param ftlibrary freetype library object
* \param family default font family * \param family default font family
* \param path default font path * \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 * \return pointer to fontconfig private data
*/ */
fc_instance_t *fontconfig_init(ass_library_t *library, fc_instance_t *fontconfig_init(ass_library_t *library,
FT_Library ftlibrary, const char *family, FT_Library ftlibrary, const char *family,
const char *path, int fc, const char *config) const char *path, int fc, const char *config,
int update)
{ {
int rc; int rc;
fc_instance_t *priv = calloc(1, sizeof(fc_instance_t)); fc_instance_t *priv = calloc(1, sizeof(fc_instance_t));
@ -444,20 +448,18 @@ fc_instance_t *fontconfig_init(ass_library_t *library,
goto exit; goto exit;
} }
if (config) { if (!config)
priv->config = FcConfigCreate(); config = (char *) FcConfigFilename(NULL);
rc = FcConfigParseAndLoad(priv->config, (unsigned char *)config,
FcTrue); priv->config = FcConfigCreate();
rc = FcConfigParseAndLoad(priv->config, (unsigned char *) config, FcTrue);
if (rc && update) {
FcConfigBuildFonts(priv->config); FcConfigBuildFonts(priv->config);
FcConfigSetCurrent(priv->config);
} else {
rc = FcInit();
assert(rc);
priv->config = FcConfigGetCurrent();
} }
if (!rc || !priv->config) { if (!rc || !priv->config) {
ass_msg(library, MSGL_FATAL, "%s failed", "FcInitLoadConfigAndFonts"); ass_msg(library, MSGL_FATAL, "%s failed", "FcInitLoadConfigAndFonts");
FcConfigDestroy(priv->config);
goto exit; goto exit;
} }
@ -507,13 +509,18 @@ fc_instance_t *fontconfig_init(ass_library_t *library,
} }
priv->family_default = family ? strdup(family) : NULL; priv->family_default = family ? strdup(family) : NULL;
exit: exit:
priv->path_default = path ? strdup(path) : NULL; priv->path_default = path ? strdup(path) : NULL;
priv->index_default = 0; priv->index_default = 0;
return priv; return priv;
} }
int fontconfig_update(fc_instance_t *priv)
{
return FcConfigBuildFonts(priv->config);
}
#else /* CONFIG_FONTCONFIG */ #else /* CONFIG_FONTCONFIG */
char *fontconfig_select(fc_instance_t *priv, const char *family, char *fontconfig_select(fc_instance_t *priv, const char *family,
@ -526,7 +533,8 @@ char *fontconfig_select(fc_instance_t *priv, const char *family,
fc_instance_t *fontconfig_init(ass_library_t *library, fc_instance_t *fontconfig_init(ass_library_t *library,
FT_Library ftlibrary, const char *family, FT_Library ftlibrary, const char *family,
const char *path, int fc) const char *path, int fc, const char *config,
int update)
{ {
fc_instance_t *priv; fc_instance_t *priv;
@ -540,6 +548,11 @@ fc_instance_t *fontconfig_init(ass_library_t *library,
return priv; return priv;
} }
int fontconfig_update(fc_instance_t *priv)
{
// Do nothing
}
#endif #endif
void fontconfig_done(fc_instance_t *priv) void fontconfig_done(fc_instance_t *priv)

View file

@ -23,6 +23,7 @@
#include <stdint.h> #include <stdint.h>
#include "ass_types.h" #include "ass_types.h"
#include "ass.h"
#include <ft2build.h> #include <ft2build.h>
#include FT_FREETYPE_H #include FT_FREETYPE_H
@ -34,11 +35,13 @@ typedef struct fc_instance_s fc_instance_t;
fc_instance_t *fontconfig_init(ass_library_t *library, fc_instance_t *fontconfig_init(ass_library_t *library,
FT_Library ftlibrary, const char *family, FT_Library ftlibrary, const char *family,
const char *path, int fc, const char *config); const char *path, int fc, const char *config,
int update);
char *fontconfig_select(ass_library_t *library, fc_instance_t *priv, char *fontconfig_select(ass_library_t *library, fc_instance_t *priv,
const char *family, int treat_family_as_pattern, const char *family, int treat_family_as_pattern,
unsigned bold, unsigned italic, int *index, unsigned bold, unsigned italic, int *index,
uint32_t code); uint32_t code);
void fontconfig_done(fc_instance_t *priv); void fontconfig_done(fc_instance_t *priv);
int fontconfig_update(fc_instance_t *priv);
#endif /* LIBASS_FONTCONFIG_H */ #endif /* LIBASS_FONTCONFIG_H */

View file

@ -44,6 +44,8 @@
#define MAX_BE 127 #define MAX_BE 127
#define SUBPIXEL_MASK 63 #define SUBPIXEL_MASK 63
#define SUBPIXEL_ACCURACY 7 // d6 mask for subpixel accuracy adjustment #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; static int last_render_id = 0;
@ -59,6 +61,11 @@ typedef struct double_vector_s {
double y; double y;
} double_vector_t; } double_vector_t;
typedef struct free_list_s {
void *object;
struct free_list_s *next;
} free_list_t;
typedef struct ass_settings_s { typedef struct ass_settings_s {
int frame_width; int frame_width;
int frame_height; int frame_height;
@ -174,6 +181,8 @@ typedef struct render_context_s {
double shadow_y; double shadow_y;
int drawing_mode; // not implemented; when != 0 text is discarded, except for style override tags int drawing_mode; // not implemented; when != 0 text is discarded, except for style override tags
ass_drawing_t *drawing; // current drawing ass_drawing_t *drawing; // current drawing
ass_drawing_t *clip_drawing;// clip vector
int clip_drawing_mode; // 0 = regular clip, 1 = inverse clip
effect_t effect_type; effect_t effect_type;
int effect_timing; int effect_timing;
@ -199,6 +208,8 @@ typedef struct cache_store_s {
hashmap_t *glyph_cache; hashmap_t *glyph_cache;
hashmap_t *bitmap_cache; hashmap_t *bitmap_cache;
hashmap_t *composite_cache; hashmap_t *composite_cache;
size_t glyph_max;
size_t bitmap_max_size;
} cache_store_t; } cache_store_t;
struct ass_renderer_s { struct ass_renderer_s {
@ -230,6 +241,9 @@ struct ass_renderer_s {
render_context_t state; render_context_t state;
text_info_t text_info; text_info_t text_info;
cache_store_t cache; cache_store_t cache;
free_list_t *free_head;
free_list_t *free_tail;
}; };
struct render_priv_s { struct render_priv_s {
@ -309,6 +323,8 @@ ass_renderer_t *ass_renderer_init(ass_library_t *library)
priv->cache.bitmap_cache = ass_bitmap_cache_init(library); priv->cache.bitmap_cache = ass_bitmap_cache_init(library);
priv->cache.composite_cache = ass_composite_cache_init(library); priv->cache.composite_cache = ass_composite_cache_init(library);
priv->cache.glyph_cache = ass_glyph_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_glyphs = MAX_GLYPHS_INITIAL;
priv->text_info.max_lines = MAX_LINES_INITIAL; priv->text_info.max_lines = MAX_LINES_INITIAL;
@ -325,6 +341,28 @@ ass_renderer_t *ass_renderer_init(ass_library_t *library)
return priv; return priv;
} }
void ass_set_cache_limits(ass_renderer_t *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_t *render_priv)
{
if (render_priv->free_head) {
free_list_t *item = render_priv->free_head;
while(item) {
free_list_t *oi = item;
free(item->object);
item = item->next;
free(oi);
}
render_priv->free_head = NULL;
}
}
void ass_renderer_done(ass_renderer_t *render_priv) void ass_renderer_done(ass_renderer_t *render_priv)
{ {
ass_font_cache_done(render_priv->cache.font_cache); ass_font_cache_done(render_priv->cache.font_cache);
@ -349,6 +387,8 @@ void ass_renderer_done(ass_renderer_t *render_priv)
free(render_priv->settings.default_font); free(render_priv->settings.default_font);
free(render_priv->settings.default_family); free(render_priv->settings.default_family);
free_list_clear(render_priv);
} }
/** /**
@ -663,6 +703,125 @@ render_overlap(ass_renderer_t *render_priv, ass_image_t **last_tail,
cache_add_composite(render_priv->cache.composite_cache, &hk, &chv); cache_add_composite(render_priv->cache.composite_cache, &hk, &chv);
} }
static void free_list_add(ass_renderer_t *render_priv, void *object)
{
if (!render_priv->free_head) {
render_priv->free_head = calloc(1, sizeof(free_list_t));
render_priv->free_head->object = object;
render_priv->free_tail = render_priv->free_head;
} else {
free_list_t *l = calloc(1, sizeof(free_list_t));
l->object = object;
render_priv->free_tail->next = l;
render_priv->free_tail = render_priv->free_tail->next;
}
}
/**
* 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_t *render_priv,
ass_image_t *head)
{
FT_Glyph glyph;
FT_BitmapGlyph clip_bm;
ass_image_t *cur;
ass_drawing_t *drawing = render_priv->state.clip_drawing;
if (!drawing)
return;
// Rasterize it
FT_Glyph_Copy((FT_Glyph) drawing->glyph, &glyph);
FT_Glyph_To_Bitmap(&glyph, FT_RENDER_MODE_NORMAL, 0, 1);
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);
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);
ass_drawing_free(render_priv->state.clip_drawing);
render_priv->state.clip_drawing = 0;
}
/** /**
* \brief Convert text_info_t struct to ass_image_t list * \brief Convert text_info_t struct to ass_image_t list
* Splits glyphs in halves when needed (for \kf karaoke). * Splits glyphs in halves when needed (for \kf karaoke).
@ -760,6 +919,8 @@ static ass_image_t *render_text(ass_renderer_t *render_priv, int dst_x,
} }
*tail = 0; *tail = 0;
blend_vector_clip(render_priv, head);
return head; return head;
} }
@ -1018,6 +1179,53 @@ interpolate_alpha(long long now,
return a; return a;
} }
#define skip_to(x) while ((*p != (x)) && (*p != '}') && (*p != 0)) { ++p;}
#define skip(x) if (*p == (x)) ++p; else { return p; }
#define skipopt(x) if (*p == (x)) { ++p; }
/**
* Parse a vector clip into an outline, using the proper scaling
* parameters. Translate it to correct for screen borders, if needed.
*/
static char *parse_vector_clip(ass_renderer_t *render_priv, char *p)
{
int scale = 1;
int res = 0;
ass_drawing_t *drawing;
render_priv->state.clip_drawing = ass_drawing_new(
render_priv->fontconfig_priv,
render_priv->state.font,
render_priv->settings.hinting,
render_priv->ftlibrary);
drawing = render_priv->state.clip_drawing;
skipopt('(');
res = mystrtoi(&p, &scale);
skipopt(',')
if (!res)
scale = 1;
drawing->scale = scale;
drawing->scale_x = render_priv->font_scale_x * render_priv->font_scale;
drawing->scale_y = render_priv->font_scale;
while (*p != ')' && *p != '}' && p != 0)
ass_drawing_add_char(drawing, *p++);
skipopt(')');
ass_drawing_parse(drawing, 1);
// We need to translate the clip according to screen borders
if (render_priv->settings.left_margin != 0 ||
render_priv->settings.top_margin != 0) {
FT_Vector trans = {
.x = int_to_d6(render_priv->settings.left_margin),
.y = -int_to_d6(render_priv->settings.top_margin),
};
FT_Outline_Translate(&drawing->glyph->outline, trans.x, trans.y);
}
ass_msg(render_priv->library, MSGL_DBG2,
"Parsed vector clip: scale %d, scales (%f, %f) string [%s]\n",
scale, drawing->scale_x, drawing->scale_y, drawing->text);
return p;
}
static void reset_render_context(ass_renderer_t *); static void reset_render_context(ass_renderer_t *);
/** /**
@ -1027,9 +1235,6 @@ static void reset_render_context(ass_renderer_t *);
*/ */
static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr) static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
{ {
#define skip_to(x) while ((*p != (x)) && (*p != '}') && (*p != 0)) { ++p;}
#define skip(x) if (*p == (x)) ++p; else { return p; }
skip_to('\\'); skip_to('\\');
skip('\\'); skip('\\');
if ((*p == '}') || (*p == 0)) if ((*p == '}') || (*p == 0))
@ -1081,15 +1286,16 @@ static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
} else if (mystrcmp(&p, "iclip")) { } else if (mystrcmp(&p, "iclip")) {
int x0, y0, x1, y1; int x0, y0, x1, y1;
int res = 1; int res = 1;
skip('('); char *start = p;
skipopt('(');
res &= mystrtoi(&p, &x0); res &= mystrtoi(&p, &x0);
skip(','); skipopt(',');
res &= mystrtoi(&p, &y0); res &= mystrtoi(&p, &y0);
skip(','); skipopt(',');
res &= mystrtoi(&p, &x1); res &= mystrtoi(&p, &x1);
skip(','); skipopt(',');
res &= mystrtoi(&p, &y1); res &= mystrtoi(&p, &y1);
skip(')'); skipopt(')');
if (res) { if (res) {
render_priv->state.clip_x0 = render_priv->state.clip_x0 =
render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr; render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr;
@ -1100,6 +1306,9 @@ static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
render_priv->state.clip_y1 = render_priv->state.clip_y1 =
render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr; render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr;
render_priv->state.clip_mode = 1; render_priv->state.clip_mode = 1;
} else if (!render_priv->state.clip_drawing) {
p = parse_vector_clip(render_priv, start);
render_priv->state.clip_drawing_mode = 1;
} else } else
render_priv->state.clip_mode = 0; render_priv->state.clip_mode = 0;
} else if (mystrcmp(&p, "blur")) { } else if (mystrcmp(&p, "blur")) {
@ -1398,17 +1607,18 @@ static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
skip_to(')'); // in case there is some unknown tag or a comment skip_to(')'); // in case there is some unknown tag or a comment
skip(')'); skip(')');
} else if (mystrcmp(&p, "clip")) { } else if (mystrcmp(&p, "clip")) {
char *start = p;
int x0, y0, x1, y1; int x0, y0, x1, y1;
int res = 1; int res = 1;
skip('('); skipopt('(');
res &= mystrtoi(&p, &x0); res &= mystrtoi(&p, &x0);
skip(','); skipopt(',');
res &= mystrtoi(&p, &y0); res &= mystrtoi(&p, &y0);
skip(','); skipopt(',');
res &= mystrtoi(&p, &x1); res &= mystrtoi(&p, &x1);
skip(','); skipopt(',');
res &= mystrtoi(&p, &y1); res &= mystrtoi(&p, &y1);
skip(')'); skipopt(')');
if (res) { if (res) {
render_priv->state.clip_x0 = render_priv->state.clip_x0 =
render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr; render_priv->state.clip_x0 * (1 - pwr) + x0 * pwr;
@ -1418,6 +1628,10 @@ static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr; render_priv->state.clip_y0 * (1 - pwr) + y0 * pwr;
render_priv->state.clip_y1 = render_priv->state.clip_y1 =
render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr; render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr;
// Might be a vector clip
} else if (!render_priv->state.clip_drawing) {
p = parse_vector_clip(render_priv, start);
render_priv->state.clip_drawing_mode = 0;
} else { } else {
render_priv->state.clip_x0 = 0; render_priv->state.clip_x0 = 0;
render_priv->state.clip_y0 = 0; render_priv->state.clip_y0 = 0;
@ -1556,6 +1770,7 @@ static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
return p; return p;
#undef skip #undef skip
#undef skipopt
#undef skip_to #undef skip_to
} }
@ -1856,7 +2071,7 @@ static void fix_freetype_stroker(FT_OutlineGlyph glyph, int border_x,
static void stroke_outline_glyph(ass_renderer_t *render_priv, static void stroke_outline_glyph(ass_renderer_t *render_priv,
FT_OutlineGlyph *glyph, int sx, int sy) FT_OutlineGlyph *glyph, int sx, int sy)
{ {
if (sx <= 0 || sy <= 0) if (sx <= 0 && sy <= 0)
return; return;
fix_freetype_stroker(*glyph, sx, sy); fix_freetype_stroker(*glyph, sx, sy);
@ -1948,7 +2163,7 @@ get_outline_glyph(ass_renderer_t *render_priv, int symbol,
} else { } else {
glyph_hash_val_t v; glyph_hash_val_t v;
if (drawing->hash) { if (drawing->hash) {
ass_drawing_parse(drawing); ass_drawing_parse(drawing, 0);
FT_Glyph_Copy((FT_Glyph) drawing->glyph, &info->glyph); FT_Glyph_Copy((FT_Glyph) drawing->glyph, &info->glyph);
} else { } else {
info->glyph = info->glyph =
@ -2078,14 +2293,15 @@ static void measure_text(ass_renderer_t *render_priv)
text_info_t *text_info = &render_priv->text_info; text_info_t *text_info = &render_priv->text_info;
int cur_line = 0; int cur_line = 0;
double max_asc = 0., max_desc = 0.; double max_asc = 0., max_desc = 0.;
glyph_info_t *last = NULL;
int i; int i;
int empty_line = 1; int empty_line = 1;
text_info->height = 0.; text_info->height = 0.;
for (i = 0; i < text_info->length + 1; ++i) { for (i = 0; i < text_info->length + 1; ++i) {
if ((i == text_info->length) || text_info->glyphs[i].linebreak) { if ((i == text_info->length) || text_info->glyphs[i].linebreak) {
if (empty_line && cur_line > 0) { if (empty_line && cur_line > 0 && last && i < text_info->length) {
max_asc = text_info->lines[cur_line - 1].asc / 2.0; max_asc = d6_to_double(last->asc) / 2.0;
max_desc = text_info->lines[cur_line - 1].desc / 2.0; max_desc = d6_to_double(last->desc) / 2.0;
} }
text_info->lines[cur_line].asc = max_asc; text_info->lines[cur_line].asc = max_asc;
text_info->lines[cur_line].desc = max_desc; text_info->lines[cur_line].desc = max_desc;
@ -2101,6 +2317,8 @@ static void measure_text(ass_renderer_t *render_priv)
max_asc = d6_to_double(cur->asc); max_asc = d6_to_double(cur->asc);
if (d6_to_double(cur->desc) > max_desc) if (d6_to_double(cur->desc) > max_desc)
max_desc = d6_to_double(cur->desc); max_desc = d6_to_double(cur->desc);
if (cur->symbol != '\n' && cur->symbol != 0)
last = cur;
} }
} }
text_info->height += text_info->height +=
@ -2668,6 +2886,7 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
} }
} }
if (text_info->length == 0) { if (text_info->length == 0) {
// no valid symbols in the event; this can be smth like {comment} // no valid symbols in the event; this can be smth like {comment}
free_render_context(render_priv); free_render_context(render_priv);
@ -2719,7 +2938,7 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
last_glyph--; last_glyph--;
width = d6_to_double( width = d6_to_double(
last_glyph->pos.x + last_glyph->advance.x - last_glyph->pos.x + last_glyph->advance.x -
first_glyph->pos.x); first_glyph->pos.x);
if (halign == HALIGN_LEFT) { // left aligned, no action if (halign == HALIGN_LEFT) { // left aligned, no action
shift = 0; shift = 0;
@ -2769,7 +2988,7 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
} else if (valign == VALIGN_CENTER) { // midtitle } else if (valign == VALIGN_CENTER) { // midtitle
double scr_y = double scr_y =
y2scr(render_priv, render_priv->track->PlayResY / 2.0); 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 } else { // subtitle
double scr_y; double scr_y;
if (valign != VALIGN_SUB) if (valign != VALIGN_SUB)
@ -2877,7 +3096,7 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
g->hash_key.advance.x = g->hash_key.advance.x =
double_to_d6(device_x - (int) device_x + double_to_d6(device_x - (int) device_x +
d6_to_double(g->pos.x & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY; 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 + double_to_d6(device_y - (int) device_y +
d6_to_double(g->pos.y & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY; d6_to_double(g->pos.y & SUBPIXEL_MASK)) & ~SUBPIXEL_ACCURACY;
get_bitmap_glyph(render_priv, text_info->glyphs + i); get_bitmap_glyph(render_priv, text_info->glyphs + i);
@ -2984,8 +3203,9 @@ void ass_set_line_spacing(ass_renderer_t *priv, double line_spacing)
priv->settings.line_spacing = line_spacing; priv->settings.line_spacing = line_spacing;
} }
int ass_set_fonts(ass_renderer_t *priv, const char *default_font, void ass_set_fonts(ass_renderer_t *priv, const char *default_font,
const char *default_family, int fc, const char *config) const char *default_family, int fc, const char *config,
int update)
{ {
if (priv->settings.default_font) if (priv->settings.default_font)
free(priv->settings.default_font); free(priv->settings.default_font);
@ -3002,9 +3222,12 @@ int ass_set_fonts(ass_renderer_t *priv, const char *default_font,
fontconfig_done(priv->fontconfig_priv); fontconfig_done(priv->fontconfig_priv);
priv->fontconfig_priv = priv->fontconfig_priv =
fontconfig_init(priv->library, priv->ftlibrary, default_family, 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_t *render_priv)
{
return fontconfig_update(render_priv->fontconfig_priv);
} }
/** /**
@ -3014,15 +3237,18 @@ static int
ass_start_frame(ass_renderer_t *render_priv, ass_track_t *track, ass_start_frame(ass_renderer_t *render_priv, ass_track_t *track,
long long now) long long now)
{ {
if (render_priv->library != track->library)
return 1;
ass_settings_t *settings_priv = &render_priv->settings; ass_settings_t *settings_priv = &render_priv->settings;
cache_store_t *cache = &render_priv->cache;
if (!render_priv->settings.frame_width if (!render_priv->settings.frame_width
&& !render_priv->settings.frame_height) && !render_priv->settings.frame_height)
return 1; // library not initialized return 1; // library not initialized
if (render_priv->library != track->library)
return 1;
free_list_clear(render_priv);
if (track->n_events == 0) if (track->n_events == 0)
return 1; // nothing to do return 1; // nothing to do
@ -3063,6 +3289,24 @@ ass_start_frame(ass_renderer_t *render_priv, ass_track_t *track,
render_priv->prev_images_root = render_priv->images_root; render_priv->prev_images_root = render_priv->images_root;
render_priv->images_root = 0; 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; return 0;
} }

View file

@ -30,7 +30,7 @@
#define HALIGN_CENTER 2 #define HALIGN_CENTER 2
#define HALIGN_RIGHT 3 #define HALIGN_RIGHT 3
/// ass Style: line /* ASS Style: line */
typedef struct ass_style_s { typedef struct ass_style_s {
char *Name; char *Name;
char *FontName; char *FontName;
@ -54,15 +54,16 @@ typedef struct ass_style_s {
int MarginL; int MarginL;
int MarginR; int MarginR;
int MarginV; int MarginV;
// int AlphaLevel;
int Encoding; int Encoding;
int treat_fontname_as_pattern; int treat_fontname_as_pattern;
} ass_style_t; } ass_style_t;
typedef struct render_priv_s render_priv_t; 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 * 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 { typedef struct ass_event_s {
long long Start; // ms long long Start; // ms
long long Duration; // ms long long Duration; // ms
@ -81,26 +82,31 @@ typedef struct ass_event_s {
} ass_event_t; } ass_event_t;
typedef struct parser_priv_s parser_priv_t; typedef struct parser_priv_s parser_priv_t;
typedef struct ass_library_s ass_library_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) * ass track represent either an external script or a matroska subtitle stream
* (no real difference between them); it can be used in rendering after the
* headers are parsed (i.e. events format line read).
*/
typedef struct ass_track_s { typedef struct ass_track_s {
int n_styles; // amount used int n_styles; // amount used
int max_styles; // amount allocated int max_styles; // amount allocated
int n_events; int n_events;
int max_events; int max_events;
ass_style_t *styles; // array of styles, max_styles length, n_styles used ass_style_t *styles; // array of styles, max_styles length, n_styles used
ass_event_t *events; // the same as styles ass_event_t *events; // the same as styles
char *style_format; // style format line (everything after "Format: ") char *style_format; // style format line (everything after "Format: ")
char *event_format; // event format line char *event_format; // event format line
enum { TRACK_TYPE_UNKNOWN = enum {
0, TRACK_TYPE_ASS, TRACK_TYPE_SSA } track_type; TRACK_TYPE_UNKNOWN = 0,
TRACK_TYPE_ASS,
TRACK_TYPE_SSA
} track_type;
// script header fields // Script header fields
int PlayResX; int PlayResX;
int PlayResY; int PlayResY;
double Timer; double Timer;
@ -108,11 +114,11 @@ typedef struct ass_track_s {
char ScaledBorderAndShadow; char ScaledBorderAndShadow;
int default_style; // index of default style int default_style; // index of default style
char *name; // file name in case of external subs, 0 for streams char *name; // file name in case of external subs, 0 for streams
ass_library_t *library; ass_library_t *library;
parser_priv_t *parser_priv; parser_priv_t *parser_priv;
} ass_track_t; } ass_track_t;
#endif /* LIBASS_TYPES_H */ #endif /* LIBASS_TYPES_H */

View file

@ -91,7 +91,7 @@ LibassSubtitlesProvider::LibassSubtitlesProvider() {
const char *config_path = NULL; const char *config_path = NULL;
#endif #endif
ass_set_fonts(ass_renderer, NULL, "Sans", 1, config_path); ass_set_fonts(ass_renderer, NULL, "Sans", 1, config_path, 1);
} }