Sync libass with r28935 from MPlayer.
Originally committed to SVN as r2761.
This commit is contained in:
parent
a9180cfcf7
commit
8b21bd2b9f
13 changed files with 360 additions and 304 deletions
|
@ -347,6 +347,8 @@ void process_force_style(ass_track_t* track) {
|
|||
track->Timer = atof(token);
|
||||
else if(!strcasecmp(*fs, "WrapStyle"))
|
||||
track->WrapStyle = atoi(token);
|
||||
else if(!strcasecmp(*fs, "ScaledBorderAndShadow"))
|
||||
track->ScaledBorderAndShadow = parse_bool(token);
|
||||
|
||||
dt = strrchr(*fs, '.');
|
||||
if (dt) {
|
||||
|
@ -520,6 +522,8 @@ static int process_info_line(ass_track_t* track, char *str)
|
|||
track->Timer = atof(str + 6);
|
||||
} else if (!strncmp(str,"WrapStyle:", 10)) {
|
||||
track->WrapStyle = atoi(str + 10);
|
||||
} else if (!strncmp(str, "ScaledBorderAndShadow:", 22)) {
|
||||
track->ScaledBorderAndShadow = parse_bool(str + 22);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
|
|
@ -214,6 +214,7 @@ static bitmap_t* glyph_to_bitmap_internal(FT_Glyph glyph, int bord)
|
|||
dst += bm->w;
|
||||
}
|
||||
|
||||
FT_Done_Glyph(glyph);
|
||||
return bm;
|
||||
}
|
||||
|
||||
|
@ -255,56 +256,33 @@ static bitmap_t* fix_outline_and_shadow(bitmap_t* bm_g, bitmap_t* bm_o)
|
|||
}
|
||||
|
||||
/**
|
||||
* \brief Blur with [1,2,1] kernel
|
||||
* This simple blur is exactly the same as the one employed by vsfilter.
|
||||
* In addition, it's slightly faster than gaussian blur.
|
||||
* \brief Blur with [[1,2,1]. [2,4,2], [1,2,1]] kernel
|
||||
* This blur is the same as the one employed by vsfilter.
|
||||
*/
|
||||
static void be_blur(unsigned char *buf, ass_be_priv_t* priv, int w, int h) {
|
||||
static void be_blur(unsigned char *buf, int w, int h) {
|
||||
unsigned int x, y;
|
||||
unsigned int p;
|
||||
unsigned char *tmp_buf = priv->buf;
|
||||
const int wh = w*h;
|
||||
unsigned int old_sum, new_sum;
|
||||
|
||||
if (priv->size < wh) {
|
||||
priv->buf = realloc(priv->buf, wh);
|
||||
priv->size = wh;
|
||||
tmp_buf = priv->buf;
|
||||
for (y=0; y<h; y++) {
|
||||
old_sum = 2 * buf[y*w];
|
||||
for (x=0; x<w-1; x++) {
|
||||
new_sum = buf[y*w+x] + buf[y*w+x+1];
|
||||
buf[y*w+x] = (old_sum + new_sum) >> 2;
|
||||
old_sum = new_sum;
|
||||
}
|
||||
}
|
||||
memset(tmp_buf, 0, wh);
|
||||
|
||||
for (y=0; y<h; y++)
|
||||
for (x=1; x<w-1; x++) {
|
||||
p = buf[y*w+x-1];
|
||||
p += 2 * buf[y*w+x];
|
||||
p += buf[y*w+x+1];
|
||||
p = p >> 2;
|
||||
tmp_buf[y*w+x] = p;
|
||||
}
|
||||
|
||||
for (x=0; x<w; x++)
|
||||
for (y=1; y<h-1; y++) {
|
||||
p = tmp_buf[((y-1)*w+x)];
|
||||
p += 2 * tmp_buf[y*w+x];
|
||||
p += tmp_buf[((y+1)*w+x)];
|
||||
p = p >> 2;
|
||||
buf[y*w+x] = p;
|
||||
for (x=0; x<w; x++) {
|
||||
old_sum = 2 * buf[x];
|
||||
for (y=0; y<h-1; y++) {
|
||||
new_sum = buf[y*w+x] + buf[(y+1)*w+x];
|
||||
buf[y*w+x] = (old_sum + new_sum) >> 2;
|
||||
old_sum = new_sum;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ass_be_priv_t* ass_be_init(void) {
|
||||
ass_be_priv_t* priv;
|
||||
priv = calloc(1, sizeof(ass_be_priv_t));
|
||||
|
||||
return priv;
|
||||
}
|
||||
|
||||
void ass_be_done(ass_be_priv_t* priv) {
|
||||
if (priv->buf)
|
||||
free(priv->buf);
|
||||
free(priv);
|
||||
}
|
||||
|
||||
int glyph_to_bitmap(ass_be_priv_t* priv_be, ass_synth_priv_t* priv_blur,
|
||||
int glyph_to_bitmap(ass_synth_priv_t* priv_blur,
|
||||
FT_Glyph glyph, FT_Glyph outline_glyph, bitmap_t** bm_g,
|
||||
bitmap_t** bm_o, bitmap_t** bm_s, int be, double blur_radius)
|
||||
{
|
||||
|
@ -335,9 +313,9 @@ int glyph_to_bitmap(ass_be_priv_t* priv_be, ass_synth_priv_t* priv_blur,
|
|||
if (be) {
|
||||
while (be--) {
|
||||
if (*bm_o)
|
||||
be_blur((*bm_o)->buffer, priv_be, (*bm_o)->w, (*bm_o)->h);
|
||||
be_blur((*bm_o)->buffer, (*bm_o)->w, (*bm_o)->h);
|
||||
else
|
||||
be_blur((*bm_g)->buffer, priv_be, (*bm_g)->w, (*bm_g)->h);
|
||||
be_blur((*bm_g)->buffer, (*bm_g)->w, (*bm_g)->h);
|
||||
}
|
||||
} else {
|
||||
if (blur_radius > 0.0) {
|
||||
|
|
|
@ -28,13 +28,6 @@
|
|||
|
||||
typedef struct ass_synth_priv_s ass_synth_priv_t;
|
||||
|
||||
typedef struct ass_be_priv_s {
|
||||
int size;
|
||||
unsigned char *buf;
|
||||
} ass_be_priv_t;
|
||||
|
||||
ass_be_priv_t* ass_be_init(void);
|
||||
void ass_be_done(ass_be_priv_t* priv);
|
||||
ass_synth_priv_t* ass_synth_init(double);
|
||||
void ass_synth_done(ass_synth_priv_t* priv);
|
||||
|
||||
|
@ -53,7 +46,7 @@ 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_be_priv_t* priv_be, ass_synth_priv_t* priv_blur, FT_Glyph glyph, FT_Glyph outline_glyph, bitmap_t** bm_g, bitmap_t** bm_o, bitmap_t** bm_s, int be, double blur_radius);
|
||||
int glyph_to_bitmap(ass_synth_priv_t* priv_blur, FT_Glyph glyph, FT_Glyph outline_glyph, bitmap_t** bm_g, bitmap_t** bm_o, bitmap_t** bm_s, int be, double blur_radius);
|
||||
|
||||
void ass_free_bitmap(bitmap_t* bm);
|
||||
|
||||
|
|
|
@ -192,6 +192,8 @@ static int font_compare(void* key1, void* key2, size_t key_size) {
|
|||
return 0;
|
||||
if (a->italic != b->italic)
|
||||
return 0;
|
||||
if (a->treat_family_as_pattern != b->treat_family_as_pattern)
|
||||
return 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
|
@ -324,3 +326,53 @@ void ass_glyph_cache_reset(void)
|
|||
ass_glyph_cache_done();
|
||||
ass_glyph_cache_init();
|
||||
}
|
||||
|
||||
|
||||
//---------------------------------
|
||||
// composite cache
|
||||
|
||||
hashmap_t* composite_cache;
|
||||
|
||||
static void composite_hash_dtor(void* key, size_t key_size, void* value, size_t value_size)
|
||||
{
|
||||
composite_hash_val_t* v = value;
|
||||
free(v->a);
|
||||
free(v->b);
|
||||
free(key);
|
||||
free(value);
|
||||
}
|
||||
|
||||
void* cache_add_composite(composite_hash_key_t* key, composite_hash_val_t* val)
|
||||
{
|
||||
return hashmap_insert(composite_cache, key, val);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Get a composite bitmap from composite cache.
|
||||
* \param key hash key
|
||||
* \return requested hash val or 0 if not found
|
||||
*/
|
||||
composite_hash_val_t* cache_find_composite(composite_hash_key_t* key)
|
||||
{
|
||||
return hashmap_find(composite_cache, key);
|
||||
}
|
||||
|
||||
void ass_composite_cache_init(void)
|
||||
{
|
||||
composite_cache = hashmap_init(sizeof(composite_hash_key_t),
|
||||
sizeof(composite_hash_val_t),
|
||||
0xFFFF + 13,
|
||||
composite_hash_dtor, NULL, NULL);
|
||||
}
|
||||
|
||||
void ass_composite_cache_done(void)
|
||||
{
|
||||
hashmap_done(composite_cache);
|
||||
}
|
||||
|
||||
void ass_composite_cache_reset(void)
|
||||
{
|
||||
ass_composite_cache_done();
|
||||
ass_composite_cache_init();
|
||||
}
|
||||
|
||||
|
|
|
@ -65,6 +65,27 @@ bitmap_hash_val_t* cache_find_bitmap(bitmap_hash_key_t* key);
|
|||
void ass_bitmap_cache_reset(void);
|
||||
void ass_bitmap_cache_done(void);
|
||||
|
||||
|
||||
// Cache for composited bitmaps
|
||||
typedef struct composite_hash_key_s {
|
||||
int aw, ah, bw, bh;
|
||||
int ax, ay, bx, by;
|
||||
bitmap_hash_key_t a;
|
||||
bitmap_hash_key_t b;
|
||||
} composite_hash_key_t;
|
||||
|
||||
typedef struct composite_hash_val_s {
|
||||
unsigned char* a;
|
||||
unsigned char* b;
|
||||
} composite_hash_val_t;
|
||||
|
||||
void ass_composite_cache_init(void);
|
||||
void* cache_add_composite(composite_hash_key_t* key, composite_hash_val_t* val);
|
||||
composite_hash_val_t* cache_find_composite(composite_hash_key_t* key);
|
||||
void ass_composite_cache_reset(void);
|
||||
void ass_composite_cache_done(void);
|
||||
|
||||
|
||||
// describes an outline glyph
|
||||
typedef struct glyph_hash_key_s {
|
||||
ass_font_t* font;
|
||||
|
|
|
@ -125,7 +125,7 @@ static int add_face(void* fc_priv, ass_font_t* font, uint32_t ch)
|
|||
if (font->n_faces == ASS_FONT_MAX_FACES)
|
||||
return -1;
|
||||
|
||||
path = fontconfig_select(fc_priv, font->desc.family, font->desc.bold,
|
||||
path = fontconfig_select(fc_priv, font->desc.family, font->desc.treat_family_as_pattern, font->desc.bold,
|
||||
font->desc.italic, &index, ch);
|
||||
|
||||
mem_idx = find_font(font->library, path);
|
||||
|
@ -169,6 +169,7 @@ ass_font_t* ass_font_new(ass_library_t* library, FT_Library ftlibrary, void* fc_
|
|||
font.ftlibrary = ftlibrary;
|
||||
font.n_faces = 0;
|
||||
font.desc.family = strdup(desc->family);
|
||||
font.desc.treat_family_as_pattern = desc->treat_family_as_pattern;
|
||||
font.desc.bold = desc->bold;
|
||||
font.desc.italic = desc->italic;
|
||||
|
||||
|
|
|
@ -33,6 +33,7 @@ typedef struct ass_font_desc_s {
|
|||
char* family;
|
||||
unsigned bold;
|
||||
unsigned italic;
|
||||
int treat_family_as_pattern;
|
||||
} ass_font_desc_t;
|
||||
|
||||
#define ASS_FONT_MAX_FACES 10
|
||||
|
|
|
@ -28,7 +28,6 @@
|
|||
#include <string.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/param.h>
|
||||
#include <inttypes.h>
|
||||
#include <ft2build.h>
|
||||
#include FT_FREETYPE_H
|
||||
|
@ -71,14 +70,15 @@ struct fc_instance_s {
|
|||
* \brief Low-level font selection.
|
||||
* \param priv private data
|
||||
* \param family font family
|
||||
* \param treat_family_as_pattern treat family as fontconfig pattern
|
||||
* \param bold font weight value
|
||||
* \param italic font slant value
|
||||
* \param index out: font index inside a file
|
||||
* \param code: the character that should be present in the font, can be 0
|
||||
* \return font file path
|
||||
*/
|
||||
static char* _select_font(fc_instance_t* priv, const char* family, unsigned bold, unsigned italic, int* index,
|
||||
uint32_t code)
|
||||
static char* _select_font(fc_instance_t* priv, const char* family, int treat_family_as_pattern,
|
||||
unsigned bold, unsigned italic, int* index, uint32_t code)
|
||||
{
|
||||
FcBool rc;
|
||||
FcResult result;
|
||||
|
@ -94,34 +94,40 @@ static char* _select_font(fc_instance_t* priv, const char* family, unsigned bold
|
|||
|
||||
*index = 0;
|
||||
|
||||
pat = FcPatternCreate();
|
||||
if (treat_family_as_pattern)
|
||||
pat = FcNameParse((const FcChar8*)family);
|
||||
else
|
||||
pat = FcPatternCreate();
|
||||
|
||||
if (!pat)
|
||||
goto error;
|
||||
|
||||
FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)family);
|
||||
if (!treat_family_as_pattern) {
|
||||
FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)family);
|
||||
|
||||
// In SSA/ASS fonts are sometimes referenced by their "full name",
|
||||
// which is usually a concatenation of family name and font
|
||||
// style (ex. Ottawa Bold). Full name is available from
|
||||
// FontConfig pattern element FC_FULLNAME, but it is never
|
||||
// used for font matching.
|
||||
// Therefore, I'm removing words from the end of the name one
|
||||
// by one, and adding shortened names to the pattern. It seems
|
||||
// that the first value (full name in this case) has
|
||||
// precedence in matching.
|
||||
// An alternative approach could be to reimplement FcFontSort
|
||||
// using FC_FULLNAME instead of FC_FAMILY.
|
||||
family_cnt = 1;
|
||||
{
|
||||
char* s = strdup(family);
|
||||
char* p = s + strlen(s);
|
||||
while (--p > s)
|
||||
if (*p == ' ' || *p == '-') {
|
||||
*p = '\0';
|
||||
FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)s);
|
||||
++ family_cnt;
|
||||
}
|
||||
free(s);
|
||||
// In SSA/ASS fonts are sometimes referenced by their "full name",
|
||||
// which is usually a concatenation of family name and font
|
||||
// style (ex. Ottawa Bold). Full name is available from
|
||||
// FontConfig pattern element FC_FULLNAME, but it is never
|
||||
// used for font matching.
|
||||
// Therefore, I'm removing words from the end of the name one
|
||||
// by one, and adding shortened names to the pattern. It seems
|
||||
// that the first value (full name in this case) has
|
||||
// precedence in matching.
|
||||
// An alternative approach could be to reimplement FcFontSort
|
||||
// using FC_FULLNAME instead of FC_FAMILY.
|
||||
family_cnt = 1;
|
||||
{
|
||||
char* s = strdup(family);
|
||||
char* p = s + strlen(s);
|
||||
while (--p > s)
|
||||
if (*p == ' ' || *p == '-') {
|
||||
*p = '\0';
|
||||
FcPatternAddString(pat, FC_FAMILY, (const FcChar8*)s);
|
||||
++ family_cnt;
|
||||
}
|
||||
free(s);
|
||||
}
|
||||
}
|
||||
FcPatternAddBool(pat, FC_OUTLINE, FcTrue);
|
||||
FcPatternAddInteger(pat, FC_SLANT, italic);
|
||||
|
@ -187,7 +193,8 @@ static char* _select_font(fc_instance_t* priv, const char* family, unsigned bold
|
|||
if (result != FcResultMatch)
|
||||
r_fullname = NULL;
|
||||
|
||||
if (!(r_family && strcasecmp((const char*)r_family, family) == 0) &&
|
||||
if (!treat_family_as_pattern &&
|
||||
!(r_family && strcasecmp((const char*)r_family, family) == 0) &&
|
||||
!(r_fullname && strcasecmp((const char*)r_fullname, family) == 0))
|
||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_SelectedFontFamilyIsNotTheRequestedOne,
|
||||
(const char*)(r_fullname ? r_fullname : r_family), family);
|
||||
|
@ -224,14 +231,15 @@ static char* _select_font(fc_instance_t* priv, const char* family, unsigned bold
|
|||
* \brief Find a font. Use default family or path if necessary.
|
||||
* \param priv_ private data
|
||||
* \param family font family
|
||||
* \param treat_family_as_pattern treat family as fontconfig pattern
|
||||
* \param bold font weight value
|
||||
* \param italic font slant value
|
||||
* \param index out: font index inside a file
|
||||
* \param code: the character that should be present in the font, can be 0
|
||||
* \return font file path
|
||||
*/
|
||||
char* fontconfig_select(fc_instance_t* priv, const char* family, unsigned bold, unsigned italic, int* index,
|
||||
uint32_t code)
|
||||
char* fontconfig_select(fc_instance_t* priv, const char* family, int treat_family_as_pattern,
|
||||
unsigned bold, unsigned italic, int* index, uint32_t code)
|
||||
{
|
||||
char* res = 0;
|
||||
if (!priv->config) {
|
||||
|
@ -239,9 +247,9 @@ char* fontconfig_select(fc_instance_t* priv, const char* family, unsigned bold,
|
|||
return priv->path_default;
|
||||
}
|
||||
if (family && *family)
|
||||
res = _select_font(priv, family, bold, italic, index, code);
|
||||
res = _select_font(priv, family, treat_family_as_pattern, bold, italic, index, code);
|
||||
if (!res && priv->family_default) {
|
||||
res = _select_font(priv, priv->family_default, bold, italic, index, code);
|
||||
res = _select_font(priv, priv->family_default, 0, bold, italic, index, code);
|
||||
if (res)
|
||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingDefaultFontFamily,
|
||||
family, bold, italic, res, *index);
|
||||
|
@ -253,7 +261,7 @@ char* fontconfig_select(fc_instance_t* priv, const char* family, unsigned bold,
|
|||
family, bold, italic, res, *index);
|
||||
}
|
||||
if (!res) {
|
||||
res = _select_font(priv, "Arial", bold, italic, index, code);
|
||||
res = _select_font(priv, "Arial", 0, bold, italic, index, code);
|
||||
if (res)
|
||||
mp_msg(MSGT_ASS, MSGL_WARN, MSGTR_LIBASS_UsingArialFontFamily,
|
||||
family, bold, italic, res, *index);
|
||||
|
@ -412,7 +420,8 @@ fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, con
|
|||
char *config_dir;
|
||||
#endif
|
||||
int i;
|
||||
|
||||
|
||||
|
||||
if (!fc) {
|
||||
mp_msg(MSGT_ASS, MSGL_WARN,
|
||||
MSGTR_LIBASS_FontconfigDisabledDefaultFontWillBeUsed);
|
||||
|
@ -425,8 +434,8 @@ fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, con
|
|||
free(config_dir);
|
||||
priv->config = FcConfigCreate();
|
||||
rc = FcConfigParseAndLoad(priv->config, config_path, FcTrue);
|
||||
FcConfigBuildFonts(priv->config);
|
||||
FcConfigSetCurrent(priv->config);
|
||||
FcConfigBuildFonts(priv->config);
|
||||
FcConfigSetCurrent(priv->config);
|
||||
|
||||
if (!rc) {
|
||||
#else
|
||||
|
@ -434,7 +443,6 @@ fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, con
|
|||
assert(rc);
|
||||
|
||||
priv->config = FcConfigGetCurrent();
|
||||
|
||||
if (!priv->config) {
|
||||
#endif
|
||||
|
||||
|
@ -497,8 +505,8 @@ exit:
|
|||
|
||||
#else /* CONFIG_FONTCONFIG */
|
||||
|
||||
char* fontconfig_select(fc_instance_t* priv, const char* family, unsigned bold, unsigned italic, int* index,
|
||||
uint32_t code)
|
||||
char* fontconfig_select(fc_instance_t* priv, const char* family, int treat_family_as_pattern,
|
||||
unsigned bold, unsigned italic, int* index, uint32_t code)
|
||||
{
|
||||
*index = priv->index_default;
|
||||
return priv->path_default;
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
typedef struct fc_instance_s fc_instance_t;
|
||||
|
||||
fc_instance_t* fontconfig_init(ass_library_t* library, FT_Library ftlibrary, const char* family, const char* path, int fc);
|
||||
char* fontconfig_select(fc_instance_t* priv, const char* family, unsigned bold, unsigned italic, int* index, uint32_t code);
|
||||
char* fontconfig_select(fc_instance_t* priv, const char* family, int treat_family_as_pattern, unsigned bold, unsigned italic, int* index, uint32_t code);
|
||||
void fontconfig_done(fc_instance_t* priv);
|
||||
|
||||
#endif /* LIBASS_FONTCONFIG_H */
|
||||
|
|
|
@ -44,7 +44,9 @@
|
|||
#define MAX_GLYPHS 3000
|
||||
#define MAX_LINES 300
|
||||
#define BLUR_MAX_RADIUS 50.0
|
||||
#define MAX_BE 100
|
||||
#define ROUND(x) ((int) ((x) + .5))
|
||||
#define SUBPIXEL_MASK 56 // d6 bitmask for subpixel accuracy adjustment
|
||||
|
||||
static int last_render_id = 0;
|
||||
|
||||
|
@ -81,8 +83,7 @@ struct ass_renderer_s {
|
|||
fc_instance_t* fontconfig_priv;
|
||||
ass_settings_t settings;
|
||||
int render_id;
|
||||
ass_be_priv_t* be_priv;
|
||||
ass_synth_priv_t* synth_priv_blur;
|
||||
ass_synth_priv_t* synth_priv;
|
||||
|
||||
ass_image_t* images_root; // rendering result is stored here
|
||||
ass_image_t* prev_images_root;
|
||||
|
@ -183,6 +184,7 @@ typedef struct render_context_s {
|
|||
char* family;
|
||||
unsigned bold;
|
||||
unsigned italic;
|
||||
int treat_family_as_pattern;
|
||||
|
||||
} render_context_t;
|
||||
|
||||
|
@ -201,19 +203,11 @@ typedef struct frame_context_s {
|
|||
double border_scale;
|
||||
} frame_context_t;
|
||||
|
||||
// List of objects to free after each frame
|
||||
typedef struct free_list_s {
|
||||
int count;
|
||||
int size;
|
||||
void** obj;
|
||||
} free_list_t;
|
||||
|
||||
static ass_renderer_t* ass_renderer;
|
||||
static ass_settings_t* global_settings;
|
||||
static text_info_t text_info;
|
||||
static render_context_t render_context;
|
||||
static frame_context_t frame_context;
|
||||
static free_list_t free_list;
|
||||
|
||||
struct render_priv_s {
|
||||
int top, height;
|
||||
|
@ -277,8 +271,7 @@ ass_renderer_t* ass_renderer_init(ass_library_t* library)
|
|||
goto ass_init_exit;
|
||||
}
|
||||
|
||||
priv->be_priv = ass_be_init();
|
||||
priv->synth_priv_blur = ass_synth_init(BLUR_MAX_RADIUS);
|
||||
priv->synth_priv = ass_synth_init(BLUR_MAX_RADIUS);
|
||||
|
||||
priv->library = library;
|
||||
priv->ftlibrary = ft;
|
||||
|
@ -286,6 +279,7 @@ ass_renderer_t* ass_renderer_init(ass_library_t* library)
|
|||
|
||||
ass_font_cache_init();
|
||||
ass_bitmap_cache_init();
|
||||
ass_composite_cache_init();
|
||||
ass_glyph_cache_init();
|
||||
|
||||
text_info.glyphs = calloc(MAX_GLYPHS, sizeof(glyph_info_t));
|
||||
|
@ -297,34 +291,11 @@ ass_init_exit:
|
|||
return priv;
|
||||
}
|
||||
|
||||
static void ass_free_objects(void) {
|
||||
int i = 0;
|
||||
while (free_list.count--)
|
||||
free(free_list.obj[i++]);
|
||||
free_list.count = 0;
|
||||
}
|
||||
|
||||
static void ass_free_add(void * obj) {
|
||||
if (free_list.size == 0) {
|
||||
free_list.size = 2;
|
||||
free_list.obj = malloc(free_list.size * sizeof(void *));
|
||||
} else if (free_list.size <= free_list.count+1) {
|
||||
free_list.size *= 2;
|
||||
free_list.obj = realloc(free_list.obj, free_list.size * sizeof(void *));
|
||||
}
|
||||
free_list.obj[free_list.count++] = obj;
|
||||
}
|
||||
|
||||
static void ass_free_done(void) {
|
||||
if (free_list.obj)
|
||||
free(free_list.obj);
|
||||
free_list.obj = 0;
|
||||
}
|
||||
|
||||
void ass_renderer_done(ass_renderer_t* priv)
|
||||
{
|
||||
ass_font_cache_done();
|
||||
ass_bitmap_cache_done();
|
||||
ass_composite_cache_done();
|
||||
ass_glyph_cache_done();
|
||||
if (render_context.stroker) {
|
||||
FT_Stroker_Done(render_context.stroker);
|
||||
|
@ -332,13 +303,10 @@ void ass_renderer_done(ass_renderer_t* priv)
|
|||
}
|
||||
if (priv && priv->ftlibrary) FT_Done_FreeType(priv->ftlibrary);
|
||||
if (priv && priv->fontconfig_priv) fontconfig_done(priv->fontconfig_priv);
|
||||
if (priv && priv->synth_priv_blur) ass_synth_done(priv->synth_priv_blur);
|
||||
if (priv && priv->be_priv) ass_be_done(priv->be_priv);
|
||||
if (priv && priv->synth_priv) ass_synth_done(priv->synth_priv);
|
||||
if (priv && priv->eimg) free(priv->eimg);
|
||||
if (priv) free(priv);
|
||||
if (text_info.glyphs) free(text_info.glyphs);
|
||||
ass_free_objects();
|
||||
ass_free_done();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -445,60 +413,90 @@ static ass_image_t** render_glyph(bitmap_t* bm, int dst_x, int dst_y, uint32_t c
|
|||
* Mainly useful for translucent glyphs and especially borders, to avoid the
|
||||
* luminance adding up where they overlap (which looks ugly)
|
||||
*/
|
||||
static void render_overlap(ass_image_t** last_tail, ass_image_t** tail) {
|
||||
static void render_overlap(ass_image_t** last_tail, ass_image_t** tail, bitmap_hash_key_t *last_hash, bitmap_hash_key_t* hash) {
|
||||
int left, top, bottom, right;
|
||||
int old_left, old_top, w, h, cur_left, cur_top;
|
||||
int x, y, opos, cpos;
|
||||
char m;
|
||||
|
||||
composite_hash_key_t hk;
|
||||
composite_hash_val_t *hv;
|
||||
composite_hash_key_t *nhk;
|
||||
int ax = (*last_tail)->dst_x;
|
||||
int ay = (*last_tail)->dst_y;
|
||||
int aw = (*last_tail)->w;
|
||||
int as = (*last_tail)->stride;
|
||||
int ah = (*last_tail)->h;
|
||||
int bx = (*tail)->dst_x;
|
||||
int by = (*tail)->dst_y;
|
||||
int bw = (*tail)->w;
|
||||
int bs = (*tail)->stride;
|
||||
int bh = (*tail)->h;
|
||||
unsigned char* a;
|
||||
unsigned char* b;
|
||||
|
||||
// Calculate overlap as coordinates
|
||||
if ((*last_tail)->bitmap == (*tail)->bitmap)
|
||||
return;
|
||||
|
||||
if ((*last_tail)->color != (*tail)->color)
|
||||
return;
|
||||
|
||||
// Calculate overlap coordinates
|
||||
left = (ax > bx) ? ax : bx;
|
||||
top = (ay > by) ? ay : by;
|
||||
right = ((ax+aw) < (bx+bw)) ?
|
||||
(ax+aw) : (bx+bw);
|
||||
bottom = ((ay+ah) < (by+bh)) ?
|
||||
(ay+ah) : (by+bh);
|
||||
// Check whether overlap rect is valid
|
||||
right = ((ax+aw) < (bx+bw)) ? (ax+aw) : (bx+bw);
|
||||
bottom = ((ay+ah) < (by+bh)) ? (ay+ah) : (by+bh);
|
||||
if ((right <= left) || (bottom <= top))
|
||||
return;
|
||||
// Translate into coordinates+width/height for each bitmap
|
||||
old_left = left-(ax);
|
||||
old_top = top-(ay);
|
||||
old_left = left-ax;
|
||||
old_top = top-ay;
|
||||
w = right-left;
|
||||
h = bottom-top;
|
||||
cur_left = left-(bx);
|
||||
cur_top = top-(by);
|
||||
cur_left = left-bx;
|
||||
cur_top = top-by;
|
||||
|
||||
// Query cache
|
||||
memcpy(&hk.a, last_hash, sizeof(*last_hash));
|
||||
memcpy(&hk.b, hash, sizeof(*hash));
|
||||
hk.aw = aw;
|
||||
hk.ah = ah;
|
||||
hk.bw = bw;
|
||||
hk.bh = bh;
|
||||
hk.ax = ax;
|
||||
hk.ay = ay;
|
||||
hk.bx = bx;
|
||||
hk.by = by;
|
||||
hv = cache_find_composite(&hk);
|
||||
if (hv) {
|
||||
(*last_tail)->bitmap = hv->a;
|
||||
(*tail)->bitmap = hv->b;
|
||||
return;
|
||||
}
|
||||
|
||||
// Allocate new bitmaps and copy over data
|
||||
a = (*last_tail)->bitmap;
|
||||
b = (*tail)->bitmap;
|
||||
(*last_tail)->bitmap = malloc(aw*ah);
|
||||
ass_free_add((*last_tail)->bitmap);
|
||||
(*tail)->bitmap = malloc(bw*bh);
|
||||
ass_free_add((*tail)->bitmap);
|
||||
memcpy((*last_tail)->bitmap, a, aw*ah);
|
||||
memcpy((*tail)->bitmap, b, bw*bh);
|
||||
(*last_tail)->bitmap = malloc(as*ah);
|
||||
(*tail)->bitmap = malloc(bs*bh);
|
||||
memcpy((*last_tail)->bitmap, a, as*ah);
|
||||
memcpy((*tail)->bitmap, b, bs*bh);
|
||||
|
||||
// Composite overlapping area
|
||||
for (y=0; y<h; y++)
|
||||
for (x=0; x<w; x++) {
|
||||
opos = (old_top+y)*(aw) + (old_left+x);
|
||||
cpos = (cur_top+y)*(bw) + (cur_left+x);
|
||||
opos = (old_top+y)*(as) + (old_left+x);
|
||||
cpos = (cur_top+y)*(bs) + (cur_left+x);
|
||||
m = (a[opos] > b[cpos]) ? a[opos] : b[cpos];
|
||||
(*last_tail)->bitmap[opos] = 0;
|
||||
(*tail)->bitmap[cpos] = m;
|
||||
}
|
||||
|
||||
// Insert bitmaps into the cache
|
||||
nhk = calloc(1, sizeof(*nhk));
|
||||
memcpy(nhk, &hk, sizeof(*nhk));
|
||||
hv = calloc(1, sizeof(*hv));
|
||||
hv->a = (*last_tail)->bitmap;
|
||||
hv->b = (*tail)->bitmap;
|
||||
cache_add_composite(nhk, hv);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -514,7 +512,8 @@ static ass_image_t* render_text(text_info_t* text_info, int dst_x, int dst_y)
|
|||
ass_image_t** tail = &head;
|
||||
ass_image_t** last_tail = 0;
|
||||
ass_image_t** here_tail = 0;
|
||||
|
||||
bitmap_hash_key_t* last_hash = 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_s || (info->shadow == 0))
|
||||
|
@ -524,9 +523,15 @@ static ass_image_t* render_text(text_info_t* text_info, int dst_x, int dst_y)
|
|||
pen_y = dst_y + info->pos.y + ROUND(info->shadow * frame_context.border_scale);
|
||||
bm = info->bm_s;
|
||||
|
||||
here_tail = tail;
|
||||
tail = render_glyph(bm, pen_x, pen_y, info->c[3], 0, 1000000, tail);
|
||||
if (last_tail && tail != here_tail && ((info->c[3] & 0xff) > 0))
|
||||
render_overlap(last_tail, here_tail, last_hash, &info->hash_key);
|
||||
last_tail = here_tail;
|
||||
last_hash = &info->hash_key;
|
||||
}
|
||||
|
||||
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)
|
||||
|
@ -542,8 +547,9 @@ static ass_image_t* render_text(text_info_t* text_info, int dst_x, int dst_y)
|
|||
here_tail = tail;
|
||||
tail = render_glyph(bm, pen_x, pen_y, info->c[2], 0, 1000000, tail);
|
||||
if (last_tail && tail != here_tail && ((info->c[2] & 0xff) > 0))
|
||||
render_overlap(last_tail, here_tail);
|
||||
render_overlap(last_tail, here_tail, last_hash, &info->hash_key);
|
||||
last_tail = here_tail;
|
||||
last_hash = &info->hash_key;
|
||||
}
|
||||
}
|
||||
for (i = 0; i < text_info->length; ++i) {
|
||||
|
@ -577,17 +583,22 @@ static int x2scr(double x) {
|
|||
return x*frame_context.orig_width_nocrop / frame_context.track->PlayResX +
|
||||
FFMAX(global_settings->left_margin, 0);
|
||||
}
|
||||
static int x2scr_pos(double x) {
|
||||
static double x2scr_pos(double x) {
|
||||
return x*frame_context.orig_width / frame_context.track->PlayResX +
|
||||
global_settings->left_margin;
|
||||
}
|
||||
/**
|
||||
* \brief Mapping between script and screen coordinates
|
||||
*/
|
||||
static int y2scr(double y) {
|
||||
static double y2scr(double y) {
|
||||
return y * frame_context.orig_height_nocrop / frame_context.track->PlayResY +
|
||||
FFMAX(global_settings->top_margin, 0);
|
||||
}
|
||||
static double y2scr_pos(double y) {
|
||||
return y * frame_context.orig_height / frame_context.track->PlayResY +
|
||||
global_settings->top_margin;
|
||||
}
|
||||
|
||||
// the same for toptitles
|
||||
static int y2scr_top(double y) {
|
||||
if (global_settings->use_margins)
|
||||
|
@ -666,6 +677,7 @@ static void update_font(void)
|
|||
ass_renderer_t* priv = frame_context.ass_priv;
|
||||
ass_font_desc_t desc;
|
||||
desc.family = strdup(render_context.family);
|
||||
desc.treat_family_as_pattern = render_context.treat_family_as_pattern;
|
||||
|
||||
val = render_context.bold;
|
||||
// 0 = normal, 1 = bold, >1 = exact weight
|
||||
|
@ -816,31 +828,31 @@ static char* parse_tag(char* p, double pwr) {
|
|||
mp_msg(MSGT_ASS, MSGL_V, "stub: \\ybord%.2f\n", val);
|
||||
} else if (mystrcmp(&p, "xshad")) {
|
||||
int val;
|
||||
if (mystrtoi(&p, 10, &val))
|
||||
if (mystrtoi(&p, &val))
|
||||
mp_msg(MSGT_ASS, MSGL_V, "stub: \\xshad%d\n", val);
|
||||
} else if (mystrcmp(&p, "yshad")) {
|
||||
int val;
|
||||
if (mystrtoi(&p, 10, &val))
|
||||
if (mystrtoi(&p, &val))
|
||||
mp_msg(MSGT_ASS, MSGL_V, "stub: \\yshad%d\n", val);
|
||||
} else if (mystrcmp(&p, "fax")) {
|
||||
int val;
|
||||
if (mystrtoi(&p, 10, &val))
|
||||
if (mystrtoi(&p, &val))
|
||||
mp_msg(MSGT_ASS, MSGL_V, "stub: \\fax%d\n", val);
|
||||
} else if (mystrcmp(&p, "fay")) {
|
||||
int val;
|
||||
if (mystrtoi(&p, 10, &val))
|
||||
if (mystrtoi(&p, &val))
|
||||
mp_msg(MSGT_ASS, MSGL_V, "stub: \\fay%d\n", val);
|
||||
} else if (mystrcmp(&p, "iclip")) {
|
||||
int x0, y0, x1, y1;
|
||||
int res = 1;
|
||||
skip('(');
|
||||
res &= mystrtoi(&p, 10, &x0);
|
||||
res &= mystrtoi(&p, &x0);
|
||||
skip(',');
|
||||
res &= mystrtoi(&p, 10, &y0);
|
||||
res &= mystrtoi(&p, &y0);
|
||||
skip(',');
|
||||
res &= mystrtoi(&p, 10, &x1);
|
||||
res &= mystrtoi(&p, &x1);
|
||||
skip(',');
|
||||
res &= mystrtoi(&p, 10, &y1);
|
||||
res &= mystrtoi(&p, &y1);
|
||||
skip(')');
|
||||
mp_msg(MSGT_ASS, MSGL_V, "stub: \\iclip(%d,%d,%d,%d)\n", x0, y0, x1, y1);
|
||||
} else if (mystrcmp(&p, "blur")) {
|
||||
|
@ -890,29 +902,29 @@ static char* parse_tag(char* p, double pwr) {
|
|||
val = -1.; // reset to default
|
||||
change_border(val);
|
||||
} else if (mystrcmp(&p, "move")) {
|
||||
int x1, x2, y1, y2;
|
||||
double x1, x2, y1, y2;
|
||||
long long t1, t2, delta_t, t;
|
||||
double x, y;
|
||||
double k;
|
||||
skip('(');
|
||||
mystrtoi(&p, 10, &x1);
|
||||
mystrtod(&p, &x1);
|
||||
skip(',');
|
||||
mystrtoi(&p, 10, &y1);
|
||||
mystrtod(&p, &y1);
|
||||
skip(',');
|
||||
mystrtoi(&p, 10, &x2);
|
||||
mystrtod(&p, &x2);
|
||||
skip(',');
|
||||
mystrtoi(&p, 10, &y2);
|
||||
mystrtod(&p, &y2);
|
||||
if (*p == ',') {
|
||||
skip(',');
|
||||
mystrtoll(&p, 10, &t1);
|
||||
mystrtoll(&p, &t1);
|
||||
skip(',');
|
||||
mystrtoll(&p, 10, &t2);
|
||||
mp_msg(MSGT_ASS, MSGL_DBG2, "movement6: (%d, %d) -> (%d, %d), (%" PRId64 " .. %" PRId64 ")\n",
|
||||
mystrtoll(&p, &t2);
|
||||
mp_msg(MSGT_ASS, MSGL_DBG2, "movement6: (%f, %f) -> (%f, %f), (%" PRId64 " .. %" PRId64 ")\n",
|
||||
x1, y1, x2, y2, (int64_t)t1, (int64_t)t2);
|
||||
} else {
|
||||
t1 = 0;
|
||||
t2 = render_context.event->Duration;
|
||||
mp_msg(MSGT_ASS, MSGL_DBG2, "movement: (%d, %d) -> (%d, %d)\n", x1, y1, x2, y2);
|
||||
mp_msg(MSGT_ASS, MSGL_DBG2, "movement: (%f, %f) -> (%f, %f)\n", x1, y1, x2, y2);
|
||||
}
|
||||
skip(')');
|
||||
delta_t = t2 - t1;
|
||||
|
@ -981,7 +993,7 @@ static char* parse_tag(char* p, double pwr) {
|
|||
// FIXME: simplify
|
||||
} else if (mystrcmp(&p, "an")) {
|
||||
int val;
|
||||
if (mystrtoi(&p, 10, &val) && val) {
|
||||
if (mystrtoi(&p, &val) && val) {
|
||||
int v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment
|
||||
mp_msg(MSGT_ASS, MSGL_DBG2, "an %d\n", val);
|
||||
if (v != 0) v = 3 - v;
|
||||
|
@ -993,18 +1005,18 @@ static char* parse_tag(char* p, double pwr) {
|
|||
render_context.alignment = render_context.style->Alignment;
|
||||
} else if (mystrcmp(&p, "a")) {
|
||||
int val;
|
||||
if (mystrtoi(&p, 10, &val) && val)
|
||||
if (mystrtoi(&p, &val) && val)
|
||||
render_context.alignment = val;
|
||||
else
|
||||
render_context.alignment = render_context.style->Alignment;
|
||||
} else if (mystrcmp(&p, "pos")) {
|
||||
int v1, v2;
|
||||
double v1, v2;
|
||||
skip('(');
|
||||
mystrtoi(&p, 10, &v1);
|
||||
mystrtod(&p, &v1);
|
||||
skip(',');
|
||||
mystrtoi(&p, 10, &v2);
|
||||
mystrtod(&p, &v2);
|
||||
skip(')');
|
||||
mp_msg(MSGT_ASS, MSGL_DBG2, "pos(%d, %d)\n", v1, v2);
|
||||
mp_msg(MSGT_ASS, MSGL_DBG2, "pos(%f, %f)\n", v1, v2);
|
||||
if (render_context.evt_type != EVENT_POSITIONED) {
|
||||
render_context.evt_type = EVENT_POSITIONED;
|
||||
render_context.detect_collisions = 0;
|
||||
|
@ -1016,9 +1028,9 @@ static char* parse_tag(char* p, double pwr) {
|
|||
long long t1, t2, t3, t4;
|
||||
if (*p == 'e') ++p; // either \fad or \fade
|
||||
skip('(');
|
||||
mystrtoi(&p, 10, &a1);
|
||||
mystrtoi(&p, &a1);
|
||||
skip(',');
|
||||
mystrtoi(&p, 10, &a2);
|
||||
mystrtoi(&p, &a2);
|
||||
if (*p == ')') {
|
||||
// 2-argument version (\fad, according to specs)
|
||||
// a1 and a2 are fade-in and fade-out durations
|
||||
|
@ -1033,31 +1045,33 @@ static char* parse_tag(char* p, double pwr) {
|
|||
// 6-argument version (\fade)
|
||||
// a1 and a2 (and a3) are opacity values
|
||||
skip(',');
|
||||
mystrtoi(&p, 10, &a3);
|
||||
mystrtoi(&p, &a3);
|
||||
skip(',');
|
||||
mystrtoll(&p, 10, &t1);
|
||||
mystrtoll(&p, &t1);
|
||||
skip(',');
|
||||
mystrtoll(&p, 10, &t2);
|
||||
mystrtoll(&p, &t2);
|
||||
skip(',');
|
||||
mystrtoll(&p, 10, &t3);
|
||||
mystrtoll(&p, &t3);
|
||||
skip(',');
|
||||
mystrtoll(&p, 10, &t4);
|
||||
mystrtoll(&p, &t4);
|
||||
}
|
||||
skip(')');
|
||||
render_context.fade = interpolate_alpha(frame_context.time - render_context.event->Start, t1, t2, t3, t4, a1, a2, a3);
|
||||
} else if (mystrcmp(&p, "org")) {
|
||||
int v1, v2;
|
||||
skip('(');
|
||||
mystrtoi(&p, 10, &v1);
|
||||
mystrtoi(&p, &v1);
|
||||
skip(',');
|
||||
mystrtoi(&p, 10, &v2);
|
||||
mystrtoi(&p, &v2);
|
||||
skip(')');
|
||||
mp_msg(MSGT_ASS, MSGL_DBG2, "org(%d, %d)\n", v1, v2);
|
||||
// render_context.evt_type = EVENT_POSITIONED;
|
||||
render_context.org_x = v1;
|
||||
render_context.org_y = v2;
|
||||
render_context.have_origin = 1;
|
||||
render_context.detect_collisions = 0;
|
||||
if (!render_context.have_origin) {
|
||||
render_context.org_x = v1;
|
||||
render_context.org_y = v2;
|
||||
render_context.have_origin = 1;
|
||||
render_context.detect_collisions = 0;
|
||||
}
|
||||
} else if (mystrcmp(&p, "t")) {
|
||||
double v[3];
|
||||
int v1, v2;
|
||||
|
@ -1104,13 +1118,13 @@ static char* parse_tag(char* p, double pwr) {
|
|||
int x0, y0, x1, y1;
|
||||
int res = 1;
|
||||
skip('(');
|
||||
res &= mystrtoi(&p, 10, &x0);
|
||||
res &= mystrtoi(&p, &x0);
|
||||
skip(',');
|
||||
res &= mystrtoi(&p, 10, &y0);
|
||||
res &= mystrtoi(&p, &y0);
|
||||
skip(',');
|
||||
res &= mystrtoi(&p, 10, &x1);
|
||||
res &= mystrtoi(&p, &x1);
|
||||
skip(',');
|
||||
res &= mystrtoi(&p, 10, &y1);
|
||||
res &= mystrtoi(&p, &y1);
|
||||
skip(')');
|
||||
if (res) {
|
||||
render_context.clip_x0 = render_context.clip_x0 * (1-pwr) + x0 * pwr;
|
||||
|
@ -1153,16 +1167,16 @@ static char* parse_tag(char* p, double pwr) {
|
|||
reset_render_context();
|
||||
} else if (mystrcmp(&p, "be")) {
|
||||
int val;
|
||||
if (mystrtoi(&p, 10, &val)) {
|
||||
// Clamp to 10, since high values need excessive CPU
|
||||
if (mystrtoi(&p, &val)) {
|
||||
// Clamp to a safe upper limit, since high values need excessive CPU
|
||||
val = (val < 0) ? 0 : val;
|
||||
val = (val > 10) ? 10 : val;
|
||||
val = (val > MAX_BE) ? MAX_BE : val;
|
||||
render_context.be = val;
|
||||
} else
|
||||
render_context.be = 0;
|
||||
} else if (mystrcmp(&p, "b")) {
|
||||
int b;
|
||||
if (mystrtoi(&p, 10, &b)) {
|
||||
if (mystrtoi(&p, &b)) {
|
||||
if (pwr >= .5)
|
||||
render_context.bold = b;
|
||||
} else
|
||||
|
@ -1170,7 +1184,7 @@ static char* parse_tag(char* p, double pwr) {
|
|||
update_font();
|
||||
} else if (mystrcmp(&p, "i")) {
|
||||
int i;
|
||||
if (mystrtoi(&p, 10, &i)) {
|
||||
if (mystrtoi(&p, &i)) {
|
||||
if (pwr >= .5)
|
||||
render_context.italic = i;
|
||||
} else
|
||||
|
@ -1178,37 +1192,37 @@ static char* parse_tag(char* p, double pwr) {
|
|||
update_font();
|
||||
} else if (mystrcmp(&p, "kf") || mystrcmp(&p, "K")) {
|
||||
int val = 0;
|
||||
mystrtoi(&p, 10, &val);
|
||||
mystrtoi(&p, &val);
|
||||
render_context.effect_type = EF_KARAOKE_KF;
|
||||
if (render_context.effect_timing)
|
||||
render_context.effect_skip_timing += render_context.effect_timing;
|
||||
render_context.effect_timing = val * 10;
|
||||
} else if (mystrcmp(&p, "ko")) {
|
||||
int val = 0;
|
||||
mystrtoi(&p, 10, &val);
|
||||
mystrtoi(&p, &val);
|
||||
render_context.effect_type = EF_KARAOKE_KO;
|
||||
if (render_context.effect_timing)
|
||||
render_context.effect_skip_timing += render_context.effect_timing;
|
||||
render_context.effect_timing = val * 10;
|
||||
} else if (mystrcmp(&p, "k")) {
|
||||
int val = 0;
|
||||
mystrtoi(&p, 10, &val);
|
||||
mystrtoi(&p, &val);
|
||||
render_context.effect_type = EF_KARAOKE;
|
||||
if (render_context.effect_timing)
|
||||
render_context.effect_skip_timing += render_context.effect_timing;
|
||||
render_context.effect_timing = val * 10;
|
||||
} else if (mystrcmp(&p, "shad")) {
|
||||
int val;
|
||||
if (mystrtoi(&p, 10, &val))
|
||||
if (mystrtoi(&p, &val))
|
||||
render_context.shadow = val;
|
||||
else
|
||||
render_context.shadow = render_context.style->Shadow;
|
||||
} else if (mystrcmp(&p, "pbo")) {
|
||||
int val = 0;
|
||||
mystrtoi(&p, 10, &val); // ignored
|
||||
mystrtoi(&p, &val); // ignored
|
||||
} else if (mystrcmp(&p, "p")) {
|
||||
int val;
|
||||
if (!mystrtoi(&p, 10, &val))
|
||||
if (!mystrtoi(&p, &val))
|
||||
val = 0;
|
||||
render_context.drawing_mode = !!val;
|
||||
}
|
||||
|
@ -1347,6 +1361,7 @@ static void reset_render_context(void)
|
|||
if (render_context.family)
|
||||
free(render_context.family);
|
||||
render_context.family = strdup(render_context.style->FontName);
|
||||
render_context.treat_family_as_pattern = render_context.style->treat_fontname_as_pattern;
|
||||
render_context.bold = render_context.style->Bold;
|
||||
render_context.italic = render_context.style->Italic;
|
||||
update_font();
|
||||
|
@ -1496,8 +1511,7 @@ static void get_bitmap_glyph(glyph_info_t* info)
|
|||
transform_3d(shift, &info->glyph, &info->outline_glyph, info->frx, info->fry, info->frz);
|
||||
|
||||
// render glyph
|
||||
error = glyph_to_bitmap(ass_renderer->be_priv,
|
||||
ass_renderer->synth_priv_blur,
|
||||
error = glyph_to_bitmap(ass_renderer->synth_priv,
|
||||
info->glyph, info->outline_glyph,
|
||||
&info->bm, &info->bm_o,
|
||||
&info->bm_s, info->be, info->blur * frame_context.border_scale);
|
||||
|
@ -1790,75 +1804,46 @@ static void get_base_point(FT_BBox bbox, int alignment, int* bx, int* by)
|
|||
}
|
||||
|
||||
/**
|
||||
* \brief Multiply 4-vector by 4-matrix
|
||||
* \param a 4-vector
|
||||
* \param m 4-matrix]
|
||||
* \param b out: 4-vector
|
||||
* Calculates a * m and stores result in b
|
||||
* \brief Apply transformation to outline points of a glyph
|
||||
* Applies rotations given by frx, fry and frz and projects the points back
|
||||
* onto the screen plane.
|
||||
*/
|
||||
static inline void transform_point_3d(double *a, double *m, double *b)
|
||||
{
|
||||
b[0] = a[0] * m[0] + a[1] * m[4] + a[2] * m[8] + a[3] * m[12];
|
||||
b[1] = a[0] * m[1] + a[1] * m[5] + a[2] * m[9] + a[3] * m[13];
|
||||
b[2] = a[0] * m[2] + a[1] * m[6] + a[2] * m[10] + a[3] * m[14];
|
||||
b[3] = a[0] * m[3] + a[1] * m[7] + a[2] * m[11] + a[3] * m[15];
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Apply 3d transformation to a vector
|
||||
* \param v FreeType vector (2d)
|
||||
* \param m 4-matrix
|
||||
* Transforms v by m, projects the result back to the screen plane
|
||||
* Result is returned in v.
|
||||
*/
|
||||
static inline void transform_vector_3d(FT_Vector* v, double *m) {
|
||||
const double camera = 2500 * frame_context.border_scale; // camera distance
|
||||
const double cutoff_z = 10.;
|
||||
double a[4], b[4];
|
||||
a[0] = d6_to_double(v->x);
|
||||
a[1] = d6_to_double(v->y);
|
||||
a[2] = 0.;
|
||||
a[3] = 1.;
|
||||
transform_point_3d(a, m, b);
|
||||
/* Apply perspective projection with the following matrix:
|
||||
2500 0 0 0
|
||||
0 2500 0 0
|
||||
0 0 0 0
|
||||
0 0 8 2500
|
||||
where 2500 is camera distance, 8 - z-axis scale.
|
||||
Camera is always located in (org_x, org_y, -2500). This means
|
||||
that different subtitle events can be displayed at the same time
|
||||
using different cameras. */
|
||||
b[0] *= camera;
|
||||
b[1] *= camera;
|
||||
b[3] = 8 * b[2] + camera;
|
||||
if (b[3] < cutoff_z)
|
||||
b[3] = cutoff_z;
|
||||
v->x = double_to_d6(b[0] / b[3]);
|
||||
v->y = double_to_d6(b[1] / b[3]);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Apply 3d transformation to a glyph
|
||||
* \param glyph FreeType glyph
|
||||
* \param m 4-matrix
|
||||
* Transforms glyph by m, projects the result back to the screen plane
|
||||
* Result is returned in glyph.
|
||||
*/
|
||||
static inline void transform_glyph_3d(FT_Glyph glyph, double *m, FT_Vector shift) {
|
||||
int i;
|
||||
FT_Outline* outline = &((FT_OutlineGlyph)glyph)->outline;
|
||||
static void transform_3d_points(FT_Vector shift, FT_Glyph glyph, double frx, double fry, double frz) {
|
||||
double sx = sin(frx);
|
||||
double sy = sin(fry);
|
||||
double sz = sin(frz);
|
||||
double cx = cos(frx);
|
||||
double cy = cos(fry);
|
||||
double cz = cos(frz);
|
||||
FT_Outline *outline = &((FT_OutlineGlyph) glyph)->outline;
|
||||
FT_Vector* p = outline->points;
|
||||
double x, y, z, xx, yy, zz;
|
||||
int i;
|
||||
|
||||
for (i=0; i<outline->n_points; i++) {
|
||||
p[i].x += shift.x;
|
||||
p[i].y += shift.y;
|
||||
transform_vector_3d(p + i, m);
|
||||
p[i].x -= shift.x;
|
||||
p[i].y -= shift.y;
|
||||
}
|
||||
x = p[i].x + shift.x;
|
||||
y = p[i].y + shift.y;
|
||||
z = 0.;
|
||||
|
||||
//transform_vector_3d(&glyph->advance, m);
|
||||
xx = x*cz + y*sz;
|
||||
yy = -(x*sz - y*cz);
|
||||
zz = z;
|
||||
|
||||
x = xx;
|
||||
y = yy*cx + zz*sx;
|
||||
z = yy*sx - zz*cx;
|
||||
|
||||
xx = x*cy + z*sy;
|
||||
yy = y;
|
||||
zz = x*sy - z*cy;
|
||||
|
||||
zz = FFMAX(zz, -19000);
|
||||
|
||||
x = (xx * 20000) / (zz + 20000);
|
||||
y = (yy * 20000) / (zz + 20000);
|
||||
p[i].x = x - shift.x + 0.5;
|
||||
p[i].y = y - shift.y + 0.5;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -1873,28 +1858,18 @@ static inline void transform_glyph_3d(FT_Glyph glyph, double *m, FT_Vector shift
|
|||
*/
|
||||
static void transform_3d(FT_Vector shift, FT_Glyph* glyph, FT_Glyph* glyph2, double frx, double fry, double frz)
|
||||
{
|
||||
fry = - fry; // FreeType's y axis goes in the opposite direction
|
||||
frx = - frx;
|
||||
frz = - frz;
|
||||
if (frx != 0. || fry != 0. || frz != 0.) {
|
||||
double m[16];
|
||||
double sx = sin(frx);
|
||||
double sy = sin(fry);
|
||||
double sz = sin(frz);
|
||||
double cx = cos(frx);
|
||||
double cy = cos(fry);
|
||||
double cz = cos(frz);
|
||||
m[0] = cy * cz; m[1] = cy*sz; m[2] = -sy; m[3] = 0.0;
|
||||
m[4] = -cx*sz + sx*sy*cz; m[5] = cx*cz + sx*sy*sz; m[6] = sx*cy; m[7] = 0.0;
|
||||
m[8] = sx*sz + cx*sy*cz; m[9] = -sx*cz + cx*sy*sz; m[10] = cx*cy; m[11] = 0.0;
|
||||
m[12] = 0.0; m[13] = 0.0; m[14] = 0.0; m[15] = 1.0;
|
||||
|
||||
if (glyph && *glyph)
|
||||
transform_glyph_3d(*glyph, m, shift);
|
||||
transform_3d_points(shift, *glyph, frx, fry, frz);
|
||||
|
||||
if (glyph2 && *glyph2)
|
||||
transform_glyph_3d(*glyph2, m, shift);
|
||||
transform_3d_points(shift, *glyph2, frx, fry, frz);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* \brief Main ass rendering function, glues everything together
|
||||
* \param event event to render
|
||||
|
@ -1962,8 +1937,13 @@ static int ass_render_event(ass_event_t* event, event_images_t* event_images)
|
|||
pen.y += delta.y * render_context.scale_y;
|
||||
}
|
||||
|
||||
shift.x = pen.x & 63;
|
||||
shift.y = pen.y & 63;
|
||||
shift.x = pen.x & SUBPIXEL_MASK;
|
||||
shift.y = pen.y & SUBPIXEL_MASK;
|
||||
|
||||
if (render_context.evt_type == EVENT_POSITIONED) {
|
||||
shift.x += double_to_d6(x2scr_pos(render_context.pos_x)) & SUBPIXEL_MASK;
|
||||
shift.y -= double_to_d6(y2scr_pos(render_context.pos_y)) & SUBPIXEL_MASK;
|
||||
}
|
||||
|
||||
ass_font_set_transform(render_context.font,
|
||||
render_context.scale_x * frame_context.font_scale_x,
|
||||
|
@ -2129,15 +2109,15 @@ static int ass_render_event(ass_event_t* event, event_images_t* event_images)
|
|||
mp_msg(MSGT_ASS, MSGL_DBG2, "positioned event at %f, %f\n", render_context.pos_x, render_context.pos_y);
|
||||
get_base_point(bbox, alignment, &base_x, &base_y);
|
||||
device_x = x2scr_pos(render_context.pos_x) - base_x;
|
||||
device_y = y2scr(render_context.pos_y) - base_y;
|
||||
device_y = y2scr_pos(render_context.pos_y) - base_y;
|
||||
}
|
||||
|
||||
// fix clip coordinates (they depend on alignment)
|
||||
render_context.clip_x0 = x2scr(render_context.clip_x0);
|
||||
render_context.clip_x1 = x2scr(render_context.clip_x1);
|
||||
if (render_context.evt_type == EVENT_NORMAL ||
|
||||
render_context.evt_type == EVENT_HSCROLL ||
|
||||
render_context.evt_type == EVENT_VSCROLL) {
|
||||
render_context.clip_x0 = x2scr(render_context.clip_x0);
|
||||
render_context.clip_x1 = x2scr(render_context.clip_x1);
|
||||
if (valign == VALIGN_TOP) {
|
||||
render_context.clip_y0 = y2scr_top(render_context.clip_y0);
|
||||
render_context.clip_y1 = y2scr_top(render_context.clip_y1);
|
||||
|
@ -2149,8 +2129,10 @@ static int ass_render_event(ass_event_t* event, event_images_t* event_images)
|
|||
render_context.clip_y1 = y2scr_sub(render_context.clip_y1);
|
||||
}
|
||||
} else if (render_context.evt_type == EVENT_POSITIONED) {
|
||||
render_context.clip_y0 = y2scr(render_context.clip_y0);
|
||||
render_context.clip_y1 = y2scr(render_context.clip_y1);
|
||||
render_context.clip_x0 = x2scr_pos(render_context.clip_x0);
|
||||
render_context.clip_x1 = x2scr_pos(render_context.clip_x1);
|
||||
render_context.clip_y0 = y2scr_pos(render_context.clip_y0);
|
||||
render_context.clip_y1 = y2scr_pos(render_context.clip_y1);
|
||||
}
|
||||
|
||||
// calculate rotation parameters
|
||||
|
@ -2214,6 +2196,7 @@ static void ass_reconfigure(ass_renderer_t* priv)
|
|||
priv->render_id = ++last_render_id;
|
||||
ass_glyph_cache_reset();
|
||||
ass_bitmap_cache_reset();
|
||||
ass_composite_cache_reset();
|
||||
ass_free_images(priv->prev_images_root);
|
||||
priv->prev_images_root = 0;
|
||||
}
|
||||
|
@ -2318,8 +2301,6 @@ static int ass_start_frame(ass_renderer_t *priv, ass_track_t* track, long long n
|
|||
if (track->n_events == 0)
|
||||
return 1; // nothing to do
|
||||
|
||||
ass_free_objects();
|
||||
|
||||
frame_context.ass_priv = priv;
|
||||
frame_context.width = global_settings->frame_width;
|
||||
frame_context.height = global_settings->frame_height;
|
||||
|
@ -2338,7 +2319,10 @@ static int ass_start_frame(ass_renderer_t *priv, ass_track_t* track, long long n
|
|||
|
||||
frame_context.font_scale = global_settings->font_size_coeff *
|
||||
frame_context.orig_height / frame_context.track->PlayResY;
|
||||
frame_context.border_scale = ((double)frame_context.orig_height) / frame_context.track->PlayResY;
|
||||
if (frame_context.track->ScaledBorderAndShadow)
|
||||
frame_context.border_scale = ((double)frame_context.orig_height) / frame_context.track->PlayResY;
|
||||
else
|
||||
frame_context.border_scale = 1.;
|
||||
|
||||
frame_context.font_scale_x = 1.;
|
||||
|
||||
|
|
|
@ -58,6 +58,7 @@ typedef struct ass_style_s {
|
|||
int MarginV;
|
||||
// int AlphaLevel;
|
||||
int Encoding;
|
||||
int treat_fontname_as_pattern;
|
||||
} ass_style_t;
|
||||
|
||||
typedef struct render_priv_s render_priv_t;
|
||||
|
@ -105,6 +106,7 @@ typedef struct ass_track_s {
|
|||
int PlayResY;
|
||||
double Timer;
|
||||
int WrapStyle;
|
||||
char ScaledBorderAndShadow;
|
||||
|
||||
|
||||
int default_style; // index of default style
|
||||
|
|
|
@ -30,7 +30,7 @@
|
|||
#include "mputils.h"
|
||||
#include "ass_utils.h"
|
||||
|
||||
int mystrtoi(char** p, int base, int* res)
|
||||
int mystrtoi(char** p, int* res)
|
||||
{
|
||||
// NOTE: base argument is ignored, but not used in libass anyway
|
||||
double temp_res;
|
||||
|
@ -41,7 +41,7 @@ int mystrtoi(char** p, int base, int* res)
|
|||
else return 0;
|
||||
}
|
||||
|
||||
int mystrtoll(char** p, int base, long long* res)
|
||||
int mystrtoll(char** p, long long* res)
|
||||
{
|
||||
double temp_res;
|
||||
char* start = *p;
|
||||
|
@ -96,6 +96,17 @@ int strtocolor(char** q, uint32_t* res)
|
|||
return result;
|
||||
}
|
||||
|
||||
// Return a boolean value for a string
|
||||
char parse_bool(char* str) {
|
||||
while (*str == ' ' || *str == '\t')
|
||||
str++;
|
||||
if (!strncasecmp(str, "yes", 3))
|
||||
return 1;
|
||||
else if (strtol(str, NULL, 10) > 0)
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#if 0
|
||||
static void sprint_tag(uint32_t tag, char* dst)
|
||||
{
|
||||
|
|
|
@ -25,11 +25,12 @@
|
|||
|
||||
#include <stdint.h>
|
||||
|
||||
int mystrtoi(char** p, int base, int* res);
|
||||
int mystrtoll(char** p, int base, long long* res);
|
||||
int mystrtoi(char** p, int* res);
|
||||
int mystrtoll(char** p, long long* res);
|
||||
int mystrtou32(char** p, int base, uint32_t* res);
|
||||
int mystrtod(char** p, double* res);
|
||||
int strtocolor(char** q, uint32_t* res);
|
||||
char parse_bool(char* str);
|
||||
|
||||
static inline int d6_to_int(int x) {
|
||||
return (x + 32) >> 6;
|
||||
|
|
Loading…
Reference in a new issue