summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGrigori Goronzy <greg@kinoho.net>2014-01-08 12:30:42 -0800
committerGrigori Goronzy <greg@kinoho.net>2014-01-08 12:30:42 -0800
commitd54345a2ffff36ec24514c257a78feab58d4661a (patch)
treef9c8114782dcfc3ea304b6538b95db774d59b837
parent62df9e8345813a12834329ba6258af61e7ce0865 (diff)
parent1f5eb5ebc776a64cb7034c489353adc127d75793 (diff)
downloadlibass-d54345a2ffff36ec24514c257a78feab58d4661a.tar.bz2
libass-d54345a2ffff36ec24514c257a78feab58d4661a.tar.xz
Merge pull request #17 from astiob/compat
Various compatibility fixes + fix @font advance with HarfBuzz
-rw-r--r--libass/ass.c15
-rw-r--r--libass/ass_cache_template.h2
-rw-r--r--libass/ass_drawing.c12
-rw-r--r--libass/ass_parse.c256
-rw-r--r--libass/ass_render.c39
-rw-r--r--libass/ass_render.h3
-rw-r--r--libass/ass_shaper.c44
-rw-r--r--libass/ass_utils.c6
8 files changed, 205 insertions, 172 deletions
diff --git a/libass/ass.c b/libass/ass.c
index 262a16a..aa49246 100644
--- a/libass/ass.c
+++ b/libass/ass.c
@@ -441,7 +441,7 @@ void ass_process_force_style(ASS_Track *track)
INTVAL(Underline)
INTVAL(StrikeOut)
FPVAL(Spacing)
- INTVAL(Angle)
+ FPVAL(Angle)
INTVAL(BorderStyle)
INTVAL(Alignment)
INTVAL(MarginL)
@@ -547,6 +547,11 @@ static int process_style(ASS_Track *track, char *str)
INTVAL(Alignment)
if (track->track_type == TRACK_TYPE_ASS)
target->Alignment = numpad2align(target->Alignment);
+ // VSFilter compatibility
+ else if (target->Alignment == 8)
+ target->Alignment = 3;
+ else if (target->Alignment == 4)
+ target->Alignment = 11;
INTVAL(MarginL)
INTVAL(MarginR)
INTVAL(MarginV)
@@ -557,11 +562,15 @@ static int process_style(ASS_Track *track, char *str)
FPVAL(Shadow)
PARSE_END
}
- style->ScaleX /= 100.;
- style->ScaleY /= 100.;
+ style->ScaleX = FFMAX(style->ScaleX, 0.) / 100.;
+ style->ScaleY = FFMAX(style->ScaleY, 0.) / 100.;
+ style->Spacing = FFMAX(style->Spacing, 0.);
+ style->Outline = FFMAX(style->Outline, 0.);
+ style->Shadow = FFMAX(style->Shadow, 0.);
style->Bold = !!style->Bold;
style->Italic = !!style->Italic;
style->Underline = !!style->Underline;
+ style->StrikeOut = !!style->StrikeOut;
if (!style->Name)
style->Name = strdup("Default");
if (!style->FontName)
diff --git a/libass/ass_cache_template.h b/libass/ass_cache_template.h
index f9aab77..3d8185f 100644
--- a/libass/ass_cache_template.h
+++ b/libass/ass_cache_template.h
@@ -96,6 +96,7 @@ START(glyph, glyph_hash_key)
FTVECTOR(outline) // border width, 16.16
GENERIC(unsigned, flags) // glyph decoration flags
GENERIC(unsigned, border_style)
+ GENERIC(int, hspacing) // 16.16
END(GlyphHashKey)
START(glyph_metrics, glyph_metrics_hash_key)
@@ -114,6 +115,7 @@ START(drawing, drawing_hash_key)
GENERIC(int, pbo)
FTVECTOR(outline)
GENERIC(unsigned, border_style)
+ GENERIC(int, hspacing)
GENERIC(int, scale)
GENERIC(unsigned, hash)
STRING(text)
diff --git a/libass/ass_drawing.c b/libass/ass_drawing.c
index 03eb568..32a3803 100644
--- a/libass/ass_drawing.c
+++ b/libass/ass_drawing.c
@@ -86,7 +86,7 @@ static void drawing_prepare(ASS_Drawing *drawing)
*/
static void drawing_finish(ASS_Drawing *drawing, int raw_mode)
{
- int i, offset;
+ int i;
double pbo;
FT_BBox bbox = drawing->cbox;
FT_Outline *ol = &drawing->outline;
@@ -104,15 +104,13 @@ static void drawing_finish(ASS_Drawing *drawing, int raw_mode)
drawing->advance.x = bbox.xMax - bbox.xMin;
- pbo = drawing->pbo / (64.0 / (1 << (drawing->scale - 1)));
- drawing->desc = double_to_d6(-pbo * drawing->scale_y);
- drawing->asc = bbox.yMax - bbox.yMin + drawing->desc;
+ pbo = drawing->pbo / (1 << (drawing->scale - 1));
+ drawing->desc = double_to_d6(pbo * drawing->scale_y);
+ drawing->asc = bbox.yMax - bbox.yMin - drawing->desc;
// Place it onto the baseline
- offset = (bbox.yMax - bbox.yMin) + double_to_d6(-pbo *
- drawing->scale_y);
for (i = 0; i < ol->n_points; i++)
- ol->points[i].y += offset;
+ ol->points[i].y += drawing->asc;
}
/*
diff --git a/libass/ass_parse.c b/libass/ass_parse.c
index 8e76048..c426d13 100644
--- a/libass/ass_parse.c
+++ b/libass/ass_parse.c
@@ -81,14 +81,14 @@ void update_font(ASS_Renderer *render_priv)
val = render_priv->state.bold;
// 0 = normal, 1 = bold, >1 = exact weight
- if (val == 1 || val == -1)
+ if (val == 1)
val = 200; // bold
else if (val <= 0)
val = 80; // normal
desc.bold = val;
val = render_priv->state.italic;
- if (val == 1 || val == -1)
+ if (val == 1)
val = 110; // italic
else if (val <= 0)
val = 0; // normal
@@ -105,27 +105,6 @@ void update_font(ASS_Renderer *render_priv)
}
/**
- * \brief Calculate valid border size. Makes sure the border sizes make sense.
- *
- * \param priv renderer state object
- * \param border_x requested x border size
- * \param border_y requested y border size
- */
-void calc_border(ASS_Renderer *priv, double border_x, double border_y)
-{
- if (border_x < 0 && border_y < 0) {
- if (priv->state.border_style == 1 ||
- priv->state.border_style == 3)
- border_x = border_y = priv->state.style->Outline;
- else
- border_x = border_y = 1.;
- }
-
- priv->state.border_x = border_x;
- priv->state.border_y = border_y;
-}
-
-/**
* \brief Change border width
*
* \param render_priv renderer state object
@@ -263,26 +242,28 @@ char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr)
// New tags introduced in vsfilter 2.39
if (mystrcmp(&p, "xbord")) {
double val;
- if (mystrtod(&p, &val))
+ if (mystrtod(&p, &val)) {
val = render_priv->state.border_x * (1 - pwr) + val * pwr;
- else
- val = -1.;
- calc_border(render_priv, val, render_priv->state.border_y);
+ val = (val < 0) ? 0 : val;
+ } else
+ val = render_priv->state.style->Outline;
+ render_priv->state.border_x = val;
render_priv->state.bm_run_id++;
} else if (mystrcmp(&p, "ybord")) {
double val;
- if (mystrtod(&p, &val))
+ if (mystrtod(&p, &val)) {
val = render_priv->state.border_y * (1 - pwr) + val * pwr;
- else
- val = -1.;
- calc_border(render_priv, render_priv->state.border_x, val);
+ val = (val < 0) ? 0 : val;
+ } else
+ val = render_priv->state.style->Outline;
+ render_priv->state.border_y = val;
render_priv->state.bm_run_id++;
} else if (mystrcmp(&p, "xshad")) {
double val;
if (mystrtod(&p, &val))
val = render_priv->state.shadow_x * (1 - pwr) + val * pwr;
else
- val = 0.;
+ val = render_priv->state.style->Shadow;
render_priv->state.shadow_x = val;
render_priv->state.bm_run_id++;
} else if (mystrcmp(&p, "yshad")) {
@@ -290,7 +271,7 @@ char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr)
if (mystrtod(&p, &val))
val = render_priv->state.shadow_y * (1 - pwr) + val * pwr;
else
- val = 0.;
+ val = render_priv->state.style->Shadow;
render_priv->state.shadow_y = val;
render_priv->state.bm_run_id++;
} else if (mystrcmp(&p, "fax")) {
@@ -333,8 +314,7 @@ char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr)
} else if (!render_priv->state.clip_drawing) {
p = parse_vector_clip(render_priv, start);
render_priv->state.clip_drawing_mode = 1;
- } else
- render_priv->state.clip_mode = 0;
+ }
} else if (mystrcmp(&p, "blur")) {
double val;
if (mystrtod(&p, &val)) {
@@ -352,19 +332,23 @@ char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr)
if (tp == 'x') {
if (mystrtod(&p, &val)) {
val /= 100;
- render_priv->state.scale_x =
- render_priv->state.scale_x * (1 - pwr) + val * pwr;
+ val = render_priv->state.scale_x * (1 - pwr) + val * pwr;
+ val = (val < 0) ? 0 : val;
} else
- render_priv->state.scale_x =
- render_priv->state.style->ScaleX;
+ val = render_priv->state.style->ScaleX;
+ render_priv->state.scale_x = val;
} else if (tp == 'y') {
if (mystrtod(&p, &val)) {
val /= 100;
- render_priv->state.scale_y =
- render_priv->state.scale_y * (1 - pwr) + val * pwr;
+ val = render_priv->state.scale_y * (1 - pwr) + val * pwr;
+ val = (val < 0) ? 0 : val;
} else
- render_priv->state.scale_y =
- render_priv->state.style->ScaleY;
+ val = render_priv->state.style->ScaleY;
+ render_priv->state.scale_y = val;
+ } else {
+ --p;
+ render_priv->state.scale_x = render_priv->state.style->ScaleX;
+ render_priv->state.scale_y = render_priv->state.style->ScaleY;
}
} else if (mystrcmp(&p, "fsp")) {
double val;
@@ -375,17 +359,17 @@ char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr)
render_priv->state.hspacing = render_priv->state.style->Spacing;
} else if (mystrcmp(&p, "fs+")) {
double val;
- if (mystrtod(&p, &val)) {
- val = render_priv->state.font_size + pwr * val;
- } else
+ mystrtod(&p, &val);
+ val = render_priv->state.font_size * (1 + pwr * val / 10);
+ if (val <= 0)
val = render_priv->state.style->FontSize;
if (render_priv->state.font)
change_font_size(render_priv, val);
} else if (mystrcmp(&p, "fs-")) {
double val;
- if (mystrtod(&p, &val))
- val = render_priv->state.font_size - pwr * val;
- else
+ mystrtod(&p, &val);
+ val = render_priv->state.font_size * (1 - pwr * val / 10);
+ if (val <= 0)
val = render_priv->state.style->FontSize;
if (render_priv->state.font)
change_font_size(render_priv, val);
@@ -393,17 +377,21 @@ char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr)
double val;
if (mystrtod(&p, &val))
val = render_priv->state.font_size * (1 - pwr) + val * pwr;
- else
+ if (val <= 0)
val = render_priv->state.style->FontSize;
if (render_priv->state.font)
change_font_size(render_priv, val);
} else if (mystrcmp(&p, "bord")) {
- double val;
+ double val, xval, yval;
if (mystrtod(&p, &val)) {
- val = render_priv->state.border_x * (1 - pwr) + val * pwr;
+ xval = render_priv->state.border_x * (1 - pwr) + val * pwr;
+ yval = render_priv->state.border_y * (1 - pwr) + val * pwr;
+ xval = (xval < 0) ? 0 : xval;
+ yval = (yval < 0) ? 0 : yval;
} else
- val = -1.; // reset to default
- calc_border(render_priv, val, val);
+ xval = yval = render_priv->state.style->Outline;
+ render_priv->state.border_x = xval;
+ render_priv->state.border_y = yval;
render_priv->state.bm_run_id++;
} else if (mystrcmp(&p, "move")) {
double x1, x2, y1, y2;
@@ -487,7 +475,7 @@ char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr)
char *start = p;
char *family;
skip_to('\\');
- if (p > start) {
+ if (p > start && strncmp(start, "0", p - start)) {
family = malloc(p - start + 1);
strncpy(family, start, p - start);
family[p - start] = '\0';
@@ -506,44 +494,47 @@ char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr)
change_alpha(&render_priv->state.c[i], a, pwr);
} else {
change_alpha(&render_priv->state.c[0],
- render_priv->state.style->PrimaryColour, pwr);
+ render_priv->state.style->PrimaryColour, 1);
change_alpha(&render_priv->state.c[1],
- render_priv->state.style->SecondaryColour, pwr);
+ render_priv->state.style->SecondaryColour, 1);
change_alpha(&render_priv->state.c[2],
- render_priv->state.style->OutlineColour, pwr);
+ render_priv->state.style->OutlineColour, 1);
change_alpha(&render_priv->state.c[3],
- render_priv->state.style->BackColour, pwr);
+ render_priv->state.style->BackColour, 1);
}
render_priv->state.bm_run_id++;
// FIXME: simplify
} else if (mystrcmp(&p, "an")) {
int val;
- if (mystrtoi(&p, &val) && val) {
- int v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment
- ass_msg(render_priv->library, MSGL_DBG2, "an %d", val);
- if (v != 0)
- v = 3 - v;
- val = ((val - 1) % 3) + 1; // horizontal alignment
- val += v * 4;
- ass_msg(render_priv->library, MSGL_DBG2, "align %d", val);
- if ((render_priv->state.parsed_tags & PARSED_A) == 0) {
+ mystrtoi(&p, &val);
+ if ((render_priv->state.parsed_tags & PARSED_A) == 0) {
+ if (val >= 1 && val <= 9) {
+ int v = (val - 1) / 3; // 0, 1 or 2 for vertical alignment
+ ass_msg(render_priv->library, MSGL_DBG2, "an %d", val);
+ if (v != 0)
+ v = 3 - v;
+ val = ((val - 1) % 3) + 1; // horizontal alignment
+ val += v * 4;
+ ass_msg(render_priv->library, MSGL_DBG2, "align %d", val);
render_priv->state.alignment = val;
- render_priv->state.parsed_tags |= PARSED_A;
- }
- } else
- render_priv->state.alignment =
- render_priv->state.style->Alignment;
+ } else
+ render_priv->state.alignment =
+ render_priv->state.style->Alignment;
+ render_priv->state.parsed_tags |= PARSED_A;
+ }
} else if (mystrcmp(&p, "a")) {
int val;
- if (mystrtoi(&p, &val) && val) {
- if ((render_priv->state.parsed_tags & PARSED_A) == 0) {
- // take care of a vsfilter quirk: handle illegal \a8 like \a5
- render_priv->state.alignment = (val == 8) ? 5 : val;
- render_priv->state.parsed_tags |= PARSED_A;
- }
- } else
- render_priv->state.alignment =
- render_priv->state.style->Alignment;
+ mystrtoi(&p, &val);
+ if ((render_priv->state.parsed_tags & PARSED_A) == 0) {
+ if (val >= 1 && val <= 11)
+ // take care of a vsfilter quirk:
+ // handle illegal \a8 and \a4 like \a5
+ render_priv->state.alignment = ((val & 3) == 0) ? 5 : val;
+ else
+ render_priv->state.alignment =
+ render_priv->state.style->Alignment;
+ render_priv->state.parsed_tags |= PARSED_A;
+ }
} else if (mystrcmp(&p, "pos")) {
double v1, v2;
skip('(');
@@ -581,7 +572,7 @@ char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr)
a2 = 0;
a3 = 0xFF;
} else {
- // 6-argument version (\fade)
+ // 7-argument version (\fade)
// a1 and a2 (and a3) are opacity values
skip(',');
mystrtoi(&p, &a3);
@@ -603,13 +594,13 @@ char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr)
render_priv->state.parsed_tags |= PARSED_FADE;
}
} else if (mystrcmp(&p, "org")) {
- int v1, v2;
+ double v1, v2;
skip('(');
- mystrtoi(&p, &v1);
+ mystrtod(&p, &v1);
skip(',');
- mystrtoi(&p, &v2);
+ mystrtod(&p, &v2);
skipopt(')');
- ass_msg(render_priv->library, MSGL_DBG2, "org(%d, %d)", v1, v2);
+ ass_msg(render_priv->library, MSGL_DBG2, "org(%f, %f)", v1, v2);
if (!render_priv->state.have_origin) {
render_priv->state.org_x = v1;
render_priv->state.org_y = v2;
@@ -691,19 +682,15 @@ char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr)
} else if (!render_priv->state.clip_drawing) {
p = parse_vector_clip(render_priv, start);
render_priv->state.clip_drawing_mode = 0;
- } else {
- render_priv->state.clip_x0 = 0;
- render_priv->state.clip_y0 = 0;
- render_priv->state.clip_x1 = render_priv->track->PlayResX;
- render_priv->state.clip_y1 = render_priv->track->PlayResY;
}
} else if (mystrcmp(&p, "c")) {
uint32_t val;
int hex = render_priv->track->track_type == TRACK_TYPE_ASS;
- if (!strtocolor(render_priv->library, &p, &val, hex))
- val = render_priv->state.style->PrimaryColour;
- ass_msg(render_priv->library, MSGL_DBG2, "color: %X", val);
- change_color(&render_priv->state.c[0], val, pwr);
+ if (strtocolor(render_priv->library, &p, &val, hex))
+ change_color(&render_priv->state.c[0], val, pwr);
+ else
+ change_color(&render_priv->state.c[0],
+ render_priv->state.style->PrimaryColour, 1);
render_priv->state.bm_run_id++;
} else if ((*p >= '1') && (*p <= '4') && (++p)
&& (mystrcmp(&p, "c") || mystrcmp(&p, "a"))) {
@@ -713,7 +700,7 @@ char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr)
uint32_t val;
int hex = render_priv->track->track_type == TRACK_TYPE_ASS;
assert((n >= '1') && (n <= '4'));
- if (!strtocolor(render_priv->library, &p, &val, hex))
+ if (!strtocolor(render_priv->library, &p, &val, hex)) {
switch (n) {
case '1':
val = render_priv->state.style->PrimaryColour;
@@ -731,6 +718,8 @@ char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr)
val = 0;
break; // impossible due to assert; avoid compilation warning
}
+ pwr = 1;
+ }
switch (cmd) {
case 'c':
change_color(render_priv->state.c + cidx, val, pwr);
@@ -771,82 +760,87 @@ char *parse_tag(ASS_Renderer *render_priv, char *p, double pwr)
render_priv->state.be = 0;
render_priv->state.bm_run_id++;
} else if (mystrcmp(&p, "b")) {
- int b;
- if (mystrtoi(&p, &b)) {
- if (pwr >= .5)
- render_priv->state.bold = b;
- } else
- render_priv->state.bold = render_priv->state.style->Bold;
+ int val;
+ if (!mystrtoi(&p, &val) || !(val == 0 || val == 1 || val >= 100))
+ val = render_priv->state.style->Bold;
+ render_priv->state.bold = val;
update_font(render_priv);
} else if (mystrcmp(&p, "i")) {
- int i;
- if (mystrtoi(&p, &i)) {
- if (pwr >= .5)
- render_priv->state.italic = i;
- } else
- render_priv->state.italic = render_priv->state.style->Italic;
+ int val;
+ if (!mystrtoi(&p, &val) || !(val == 0 || val == 1))
+ val = render_priv->state.style->Italic;
+ render_priv->state.italic = val;
update_font(render_priv);
} else if (mystrcmp(&p, "kf") || mystrcmp(&p, "K")) {
- int val = 0;
- mystrtoi(&p, &val);
+ double val;
+ if (!mystrtod(&p, &val))
+ val = 100;
render_priv->state.effect_type = EF_KARAOKE_KF;
if (render_priv->state.effect_timing)
render_priv->state.effect_skip_timing +=
render_priv->state.effect_timing;
render_priv->state.effect_timing = val * 10;
} else if (mystrcmp(&p, "ko")) {
- int val = 0;
- mystrtoi(&p, &val);
+ double val;
+ if (!mystrtod(&p, &val))
+ val = 100;
render_priv->state.effect_type = EF_KARAOKE_KO;
if (render_priv->state.effect_timing)
render_priv->state.effect_skip_timing +=
render_priv->state.effect_timing;
render_priv->state.effect_timing = val * 10;
} else if (mystrcmp(&p, "k")) {
- int val = 0;
- mystrtoi(&p, &val);
+ double val;
+ if (!mystrtod(&p, &val))
+ val = 100;
render_priv->state.effect_type = EF_KARAOKE;
if (render_priv->state.effect_timing)
render_priv->state.effect_skip_timing +=
render_priv->state.effect_timing;
render_priv->state.effect_timing = val * 10;
} else if (mystrcmp(&p, "shad")) {
- double val;
+ double val, xval, yval;
if (mystrtod(&p, &val)) {
- if (render_priv->state.shadow_x == render_priv->state.shadow_y)
- val = render_priv->state.shadow_x * (1 - pwr) + val * pwr;
+ xval = render_priv->state.shadow_x * (1 - pwr) + val * pwr;
+ yval = render_priv->state.shadow_y * (1 - pwr) + val * pwr;
+ // VSFilter compatibility: clip for \shad but not for \[xy]shad
+ xval = (xval < 0) ? 0 : xval;
+ yval = (yval < 0) ? 0 : yval;
} else
- val = 0.;
- render_priv->state.shadow_x = render_priv->state.shadow_y = val;
+ xval = yval = render_priv->state.style->Shadow;
+ render_priv->state.shadow_x = xval;
+ render_priv->state.shadow_y = yval;
render_priv->state.bm_run_id++;
} else if (mystrcmp(&p, "s")) {
int val;
- if (mystrtoi(&p, &val) && val)
+ if (!mystrtoi(&p, &val) || !(val == 0 || val == 1))
+ val = render_priv->state.style->StrikeOut;
+ if (val)
render_priv->state.flags |= DECO_STRIKETHROUGH;
else
render_priv->state.flags &= ~DECO_STRIKETHROUGH;
render_priv->state.bm_run_id++;
} else if (mystrcmp(&p, "u")) {
int val;
- if (mystrtoi(&p, &val) && val)
+ if (!mystrtoi(&p, &val) || !(val == 0 || val == 1))
+ val = render_priv->state.style->Underline;
+ if (val)
render_priv->state.flags |= DECO_UNDERLINE;
else
render_priv->state.flags &= ~DECO_UNDERLINE;
render_priv->state.bm_run_id++;
} else if (mystrcmp(&p, "pbo")) {
- double val = 0;
- if (mystrtod(&p, &val))
- render_priv->state.drawing->pbo = val;
+ double val;
+ mystrtod(&p, &val);
+ render_priv->state.pbo = val;
} else if (mystrcmp(&p, "p")) {
int val;
- if (!mystrtoi(&p, &val))
- val = 0;
- if (val)
- render_priv->state.drawing->scale = val;
- render_priv->state.drawing_mode = !!val;
+ mystrtoi(&p, &val);
+ val = (val < 0) ? 0 : val;
+ render_priv->state.drawing_scale = val;
} else if (mystrcmp(&p, "q")) {
int val;
- if (!mystrtoi(&p, &val))
+ if (!mystrtoi(&p, &val) || !(val >= 0 && val <= 3))
val = render_priv->track->WrapStyle;
render_priv->state.wrap_style = val;
} else if (mystrcmp(&p, "fe")) {
diff --git a/libass/ass_render.c b/libass/ass_render.c
index 3923e52..1edb9ae 100644
--- a/libass/ass_render.c
+++ b/libass/ass_render.c
@@ -864,7 +864,8 @@ void reset_render_context(ASS_Renderer *render_priv, ASS_Style *style)
update_font(render_priv);
render_priv->state.border_style = style->BorderStyle;
- calc_border(render_priv, style->Outline, style->Outline);
+ render_priv->state.border_x = style->Outline;
+ render_priv->state.border_y = style->Outline;
change_border(render_priv, render_priv->state.border_x, render_priv->state.border_y);
render_priv->state.scale_x = style->ScaleX;
render_priv->state.scale_y = style->ScaleY;
@@ -906,7 +907,7 @@ init_render_context(ASS_Renderer *render_priv, ASS_Event *event)
render_priv->state.clip_mode = 0;
render_priv->state.detect_collisions = 1;
render_priv->state.fade = 0;
- render_priv->state.drawing_mode = 0;
+ render_priv->state.drawing_scale = 0;
render_priv->state.effect_type = EF_NONE;
render_priv->state.effect_timing = 0;
render_priv->state.effect_skip_timing = 0;
@@ -1041,6 +1042,10 @@ fill_glyph_hash(ASS_Renderer *priv, OutlineHashKey *outline_key,
key->outline.x = double_to_d16(info->border_x);
key->outline.y = double_to_d16(info->border_y);
key->border_style = info->border_style;
+ // hpacing only matters for opaque box borders (see draw_opaque_box),
+ // so for normal borders, maximize cache utility by ignoring it
+ key->hspacing =
+ info->border_style == 3 ? double_to_d16(info->hspacing) : 0;
key->hash = info->drawing->hash;
key->text = info->drawing->text;
key->pbo = info->drawing->pbo;
@@ -1060,6 +1065,8 @@ fill_glyph_hash(ASS_Renderer *priv, OutlineHashKey *outline_key,
key->outline.y = double_to_d16(info->border_y);
key->flags = info->flags;
key->border_style = info->border_style;
+ key->hspacing =
+ info->border_style == 3 ? double_to_d16(info->hspacing) : 0;
}
}
@@ -1283,9 +1290,10 @@ get_bitmap_glyph(ASS_Renderer *render_priv, GlyphInfo *info)
fay_scaled = info->fay / info->scale_x * info->scale_y;
// apply rotation
+ // use blur_scale because, like blurs, VSFilter forgets to scale this
transform_3d(shift, outline, border,
info->frx, info->fry, info->frz, fax_scaled,
- fay_scaled, render_priv->font_scale, info->asc);
+ fay_scaled, render_priv->blur_scale, info->asc);
// PAR correction scaling
FT_Matrix m = { double_to_d16(scale_x), 0,
@@ -1747,9 +1755,16 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
if (!in_tag && *p == '{') { // '\0' goes here
p++;
in_tag = 1;
+ if (render_priv->state.drawing_scale) {
+ // A drawing definition has just ended.
+ // Exit and create the drawing now lest we
+ // accidentally let it consume later text
+ // or be affected by later override tags.
+ // See Google Code issues #47 and #101.
+ break;
+ }
}
if (in_tag) {
- int prev_drawing_mode = render_priv->state.drawing_mode;
p = parse_tag(render_priv, p, 1.);
if (*p == '}') { // end of tag
p++;
@@ -1758,15 +1773,9 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
ass_msg(render_priv->library, MSGL_V,
"Unable to parse: '%.30s'", p);
}
- if (prev_drawing_mode && !render_priv->state.drawing_mode) {
- // Drawing mode was just disabled. We must exit and draw it
- // immediately, instead of letting further tags affect it.
- // See bug #47.
- break;
- }
} else {
code = get_next_char(render_priv, &p);
- if (code && render_priv->state.drawing_mode) {
+ if (code && render_priv->state.drawing_scale) {
ass_drawing_add_char(drawing, (char) code);
continue; // skip everything in drawing mode
}
@@ -1793,6 +1802,8 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
render_priv->font_scale;
drawing->scale_y = render_priv->state.scale_y *
render_priv->font_scale;
+ drawing->scale = render_priv->state.drawing_scale;
+ drawing->pbo = render_priv->state.pbo;
code = 0xfffc; // object replacement character
info->drawing = drawing;
}
@@ -1891,7 +1902,7 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
// add horizontal letter spacing
info->cluster_advance.x += double_to_d6(info->hspacing *
- render_priv->font_scale * info->scale_x);
+ render_priv->font_scale * info->orig_scale_x);
// add displacement for vertical shearing
info->cluster_advance.y += (info->fay / info->scale_x * info->scale_y) * info->cluster_advance.x;
@@ -1999,10 +2010,6 @@ ass_render_event(ASS_Renderer *render_priv, ASS_Event *event,
double width = 0;
for (i = 0; i <= text_info->length; ++i) { // (text_info->length + 1) is the end of the last line
if ((i == text_info->length) || glyphs[i].linebreak) {
- // remove letter spacing (which is included in cluster_advance)
- if (i > 0)
- width -= render_priv->state.hspacing * render_priv->font_scale *
- glyphs[i-1].scale_x;
double shift = 0;
if (halign == HALIGN_LEFT) { // left aligned, no action
shift = 0;
diff --git a/libass/ass_render.h b/libass/ass_render.h
index 3a7c6d9..a41586b 100644
--- a/libass/ass_render.h
+++ b/libass/ass_render.h
@@ -214,7 +214,8 @@ typedef struct {
double blur; // gaussian blur
double shadow_x;
double shadow_y;
- int drawing_mode; // not implemented; when != 0 text is discarded, except for style override tags
+ int drawing_scale; // currently reading: regular text if 0, drawing otherwise
+ double pbo; // drawing baseline offset
ASS_Drawing *drawing; // current drawing
ASS_Drawing *clip_drawing; // clip vector
int clip_drawing_mode; // 0 = regular clip, 1 = inverse clip
diff --git a/libass/ass_shaper.c b/libass/ass_shaper.c
index 395f9c5..4d4104e 100644
--- a/libass/ass_shaper.c
+++ b/libass/ass_shaper.c
@@ -31,9 +31,11 @@
enum {
VERT = 0,
VKNA,
- KERN
+ KERN,
+ LIGA,
+ CLIG
};
-#define NUM_FEATURES 3
+#define NUM_FEATURES 5
#endif
struct ass_shaper {
@@ -145,6 +147,10 @@ static void init_features(ASS_Shaper *shaper)
shaper->features[VKNA].end = INT_MAX;
shaper->features[KERN].tag = HB_TAG('k', 'e', 'r', 'n');
shaper->features[KERN].end = INT_MAX;
+ shaper->features[LIGA].tag = HB_TAG('l', 'i', 'g', 'a');
+ shaper->features[LIGA].end = INT_MAX;
+ shaper->features[CLIG].tag = HB_TAG('c', 'l', 'i', 'g');
+ shaper->features[CLIG].end = INT_MAX;
}
/**
@@ -152,11 +158,17 @@ static void init_features(ASS_Shaper *shaper)
*/
static void set_run_features(ASS_Shaper *shaper, GlyphInfo *info)
{
- // enable vertical substitutions for @font runs
- if (info->font->desc.vertical)
- shaper->features[VERT].value = shaper->features[VKNA].value = 1;
- else
- shaper->features[VERT].value = shaper->features[VKNA].value = 0;
+ // enable vertical substitutions for @font runs
+ if (info->font->desc.vertical)
+ shaper->features[VERT].value = shaper->features[VKNA].value = 1;
+ else
+ shaper->features[VERT].value = shaper->features[VKNA].value = 0;
+
+ // disable ligatures if horizontal spacing is non-standard
+ if (info->hspacing)
+ shaper->features[LIGA].value = shaper->features[CLIG].value = 0;
+ else
+ shaper->features[LIGA].value = shaper->features[CLIG].value = 1;
}
/**
@@ -185,7 +197,7 @@ static void update_hb_size(hb_font_t *hb_font, FT_Face face)
GlyphMetricsHashValue *
get_cached_metrics(struct ass_shaper_metrics_data *metrics, FT_Face face,
- hb_codepoint_t glyph)
+ hb_codepoint_t unicode, hb_codepoint_t glyph)
{
GlyphMetricsHashValue *val;
@@ -204,7 +216,7 @@ get_cached_metrics(struct ass_shaper_metrics_data *metrics, FT_Face face,
// if @font rendering is enabled and the glyph should be rotated,
// make cached_h_advance pick up the right advance later
- if (metrics->vertical && glyph >= VERTICAL_LOWER_BOUND)
+ if (metrics->vertical && unicode >= VERTICAL_LOWER_BOUND)
new_val.metrics.horiAdvance = new_val.metrics.vertAdvance;
val = ass_cache_put(metrics->metrics_cache, &metrics->hash_key, &new_val);
@@ -218,12 +230,17 @@ get_glyph(hb_font_t *font, void *font_data, hb_codepoint_t unicode,
hb_codepoint_t variation, hb_codepoint_t *glyph, void *user_data)
{
FT_Face face = font_data;
+ struct ass_shaper_metrics_data *metrics_priv = user_data;
if (variation)
*glyph = FT_Face_GetCharVariantIndex(face, ass_font_index_magic(face, unicode), variation);
else
*glyph = FT_Get_Char_Index(face, ass_font_index_magic(face, unicode));
+ // rotate glyph advances for @fonts while we still know the Unicode codepoints
+ if (*glyph != 0)
+ get_cached_metrics(metrics_priv, face, unicode, glyph);
+
return *glyph != 0;
}
@@ -233,7 +250,7 @@ cached_h_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
{
FT_Face face = font_data;
struct ass_shaper_metrics_data *metrics_priv = user_data;
- GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph);
+ GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
if (!metrics)
return 0;
@@ -247,7 +264,7 @@ cached_v_advance(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
{
FT_Face face = font_data;
struct ass_shaper_metrics_data *metrics_priv = user_data;
- GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph);
+ GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
if (!metrics)
return 0;
@@ -269,7 +286,7 @@ cached_v_origin(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
{
FT_Face face = font_data;
struct ass_shaper_metrics_data *metrics_priv = user_data;
- GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph);
+ GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
if (!metrics)
return 0;
@@ -306,7 +323,7 @@ cached_extents(hb_font_t *font, void *font_data, hb_codepoint_t glyph,
{
FT_Face face = font_data;
struct ass_shaper_metrics_data *metrics_priv = user_data;
- GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, glyph);
+ GlyphMetricsHashValue *metrics = get_cached_metrics(metrics_priv, face, 0, glyph);
if (!metric