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 "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;
/// a linked list of images produced by ass renderer
/* A linked list of images produced by ass renderer. */
typedef struct ass_image_s {
int w, h; // bitmap width/height
int stride; // bitmap stride
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
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
struct ass_image_s *next; // Next image, or NULL
} ass_image_t;
/// Hinting type
typedef enum { ASS_HINTING_NONE = 0,
/* Hinting type. */
typedef enum {
ASS_HINTING_NONE = 0,
ASS_HINTING_LIGHT,
ASS_HINTING_NORMAL,
ASS_HINTING_NATIVE
} ass_hinting_t;
/**
* \brief initialize the library
* \brief Initialize the library.
* \return library handle or NULL if failed
*/
ass_library_t *ass_library_init(void);
/**
* \brief finalize the library
* \brief Finalize the library
* \param priv library handle
*/
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.
*
* \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);
/**
* \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);
/**
* \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);
/**
* \brief Explicitly process style overrides for a track.
* \param track track handle
*/
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 (*msg_cb)(int, char *, va_list *, void *),
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 *);
/**
* \brief finalize the renderer
* \brief Finalize the renderer.
* \param priv renderer handle
*/
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);
/**
* \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);
/**
* \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);
/**
* \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);
/**
* \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);
/**
* \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);
/**
* \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);
/**
* \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 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.
*/
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_t *priv, const char *default_font,
const char *default_family, int fc, const char *config,
int update);
/**
* \brief render a frame, producing a list of ass_image_t
* \param priv library
* \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_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 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);
// 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 *);
/**
* \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);
/**
* \brief allocate new style
* \brief Allocate new style.
* \param track track
* \return newly allocated style id
*/
int ass_alloc_style(ass_track_t *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);
/**
* \brief delete a style
* \brief Delete a style.
* \param track track
* \param sid style id
* 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);
/**
* \brief delete an event
* \brief Delete an event.
* \param track track
* \param eid event id
* 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);
/**
* \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
@ -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);
/**
* \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,
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,
@ -195,22 +312,25 @@ ass_track_t *ass_read_file(ass_library_t *library, char *fname,
/**
* \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);
/**
* \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);
/**
* \brief Add a memory font.
* \param library library handle
* \param name attachment name
* \param data binary font data
* \param data_size data size
@ -219,18 +339,20 @@ void ass_add_font(ass_library_t *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);
/**
* \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);
#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,
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);
}

View File

@ -45,6 +45,7 @@ typedef struct hashmap_s {
hashmap_item_dtor_t item_dtor; // a destructor for hashmap key/value pairs
hashmap_key_compare_t key_compare;
hashmap_hash_t hash;
size_t cache_size;
// stats
int hit_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
* 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;
FT_BBox bbox;
@ -127,6 +127,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,10 +145,6 @@ 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);
}
/*
@ -361,7 +364,7 @@ ass_drawing_t *ass_drawing_new(void *fontconfig_priv, ass_font_t *font,
ass_drawing_t* 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;
@ -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)
{
FT_Done_Glyph((FT_Glyph) drawing->glyph);
free(drawing->text);
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.
*/
FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing)
FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing, int raw_mode)
{
int started = 0;
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);
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_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);
FT_OutlineGlyph *ass_drawing_parse(ass_drawing_t *drawing, int raw_mode);
#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) {
FT_Face face = font->faces[i];
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;
*asc = FT_MulFix(face->ascender, y_scale);
*desc = FT_MulFix(-face->descender, y_scale);
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 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)
const char *path, int fc, const char *config,
int update)
{
int rc;
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;
}
if (config) {
priv->config = FcConfigCreate();
rc = FcConfigParseAndLoad(priv->config, (unsigned char *)config,
FcTrue);
if (!config)
config = (char *) FcConfigFilename(NULL);
priv->config = FcConfigCreate();
rc = FcConfigParseAndLoad(priv->config, (unsigned char *) config, FcTrue);
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");
FcConfigDestroy(priv->config);
goto exit;
}
@ -507,13 +509,18 @@ 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(fc_instance_t *priv)
{
return FcConfigBuildFonts(priv->config);
}
#else /* CONFIG_FONTCONFIG */
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,
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;
@ -540,6 +548,11 @@ fc_instance_t *fontconfig_init(ass_library_t *library,
return priv;
}
int fontconfig_update(fc_instance_t *priv)
{
// Do nothing
}
#endif
void fontconfig_done(fc_instance_t *priv)

View File

@ -23,6 +23,7 @@
#include <stdint.h>
#include "ass_types.h"
#include "ass.h"
#include <ft2build.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,
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,
const char *family, int treat_family_as_pattern,
unsigned bold, unsigned italic, int *index,
uint32_t code);
void fontconfig_done(fc_instance_t *priv);
int fontconfig_update(fc_instance_t *priv);
#endif /* LIBASS_FONTCONFIG_H */

View File

@ -44,6 +44,8 @@
#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;
@ -59,6 +61,11 @@ typedef struct double_vector_s {
double y;
} double_vector_t;
typedef struct free_list_s {
void *object;
struct free_list_s *next;
} free_list_t;
typedef struct ass_settings_s {
int frame_width;
int frame_height;
@ -174,6 +181,8 @@ typedef struct render_context_s {
double shadow_y;
int drawing_mode; // not implemented; when != 0 text is discarded, except for style override tags
ass_drawing_t *drawing; // current drawing
ass_drawing_t *clip_drawing;// clip vector
int clip_drawing_mode; // 0 = regular clip, 1 = inverse clip
effect_t effect_type;
int effect_timing;
@ -199,6 +208,8 @@ typedef struct cache_store_s {
hashmap_t *glyph_cache;
hashmap_t *bitmap_cache;
hashmap_t *composite_cache;
size_t glyph_max;
size_t bitmap_max_size;
} cache_store_t;
struct ass_renderer_s {
@ -230,6 +241,9 @@ struct ass_renderer_s {
render_context_t state;
text_info_t text_info;
cache_store_t cache;
free_list_t *free_head;
free_list_t *free_tail;
};
struct render_priv_s {
@ -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.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;
@ -325,6 +341,28 @@ ass_renderer_t *ass_renderer_init(ass_library_t *library)
return priv;
}
void ass_set_cache_limits(ass_renderer_t *render_priv, int glyph_max,
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)
{
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_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);
}
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
* 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;
blend_vector_clip(render_priv, head);
return head;
}
@ -1018,6 +1179,53 @@ interpolate_alpha(long long now,
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 *);
/**
@ -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)
{
#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))
@ -1081,15 +1286,16 @@ static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
} else if (mystrcmp(&p, "iclip")) {
int x0, y0, x1, y1;
int res = 1;
skip('(');
char *start = p;
skipopt('(');
res &= mystrtoi(&p, &x0);
skip(',');
skipopt(',');
res &= mystrtoi(&p, &y0);
skip(',');
skipopt(',');
res &= mystrtoi(&p, &x1);
skip(',');
skipopt(',');
res &= mystrtoi(&p, &y1);
skip(')');
skipopt(')');
if (res) {
render_priv->state.clip_x0 =
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 * (1 - pwr) + y1 * pwr;
render_priv->state.clip_mode = 1;
} else if (!render_priv->state.clip_drawing) {
p = parse_vector_clip(render_priv, start);
render_priv->state.clip_drawing_mode = 1;
} else
render_priv->state.clip_mode = 0;
} else if (mystrcmp(&p, "blur")) {
@ -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(')');
} else if (mystrcmp(&p, "clip")) {
char *start = p;
int x0, y0, x1, y1;
int res = 1;
skip('(');
skipopt('(');
res &= mystrtoi(&p, &x0);
skip(',');
skipopt(',');
res &= mystrtoi(&p, &y0);
skip(',');
skipopt(',');
res &= mystrtoi(&p, &x1);
skip(',');
skipopt(',');
res &= mystrtoi(&p, &y1);
skip(')');
skipopt(')');
if (res) {
render_priv->state.clip_x0 =
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_y1 =
render_priv->state.clip_y1 * (1 - pwr) + y1 * pwr;
// Might be a vector clip
} else if (!render_priv->state.clip_drawing) {
p = parse_vector_clip(render_priv, start);
render_priv->state.clip_drawing_mode = 0;
} else {
render_priv->state.clip_x0 = 0;
render_priv->state.clip_y0 = 0;
@ -1556,6 +1770,7 @@ static char *parse_tag(ass_renderer_t *render_priv, char *p, double pwr)
return p;
#undef skip
#undef skipopt
#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,
FT_OutlineGlyph *glyph, int sx, int sy)
{
if (sx <= 0 || sy <= 0)
if (sx <= 0 && sy <= 0)
return;
fix_freetype_stroker(*glyph, sx, sy);
@ -1948,7 +2163,7 @@ get_outline_glyph(ass_renderer_t *render_priv, int symbol,
} else {
glyph_hash_val_t 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 =
@ -2078,14 +2293,15 @@ static void measure_text(ass_renderer_t *render_priv)
text_info_t *text_info = &render_priv->text_info;
int cur_line = 0;
double max_asc = 0., max_desc = 0.;
glyph_info_t *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;
@ -2101,6 +2317,8 @@ static void measure_text(ass_renderer_t *render_priv)
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 +=
@ -2668,6 +2886,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);
@ -2719,7 +2938,7 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
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 +2988,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)
@ -2877,7 +3096,7 @@ ass_render_event(ass_renderer_t *render_priv, ass_event_t *event,
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);
@ -2984,8 +3203,9 @@ void ass_set_line_spacing(ass_renderer_t *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_t *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);
@ -3002,9 +3222,12 @@ 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_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,
long long now)
{
if (render_priv->library != track->library)
return 1;
ass_settings_t *settings_priv = &render_priv->settings;
cache_store_t *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
@ -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->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;
}

View File

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

View File

@ -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);
}