diff options
author | Grigori Goronzy <greg@kinoho.net> | 2014-01-08 12:30:42 -0800 |
---|---|---|
committer | Grigori Goronzy <greg@kinoho.net> | 2014-01-08 12:30:42 -0800 |
commit | d54345a2ffff36ec24514c257a78feab58d4661a (patch) | |
tree | f9c8114782dcfc3ea304b6538b95db774d59b837 | |
parent | 62df9e8345813a12834329ba6258af61e7ce0865 (diff) | |
parent | 1f5eb5ebc776a64cb7034c489353adc127d75793 (diff) | |
download | libass-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.c | 15 | ||||
-rw-r--r-- | libass/ass_cache_template.h | 2 | ||||
-rw-r--r-- | libass/ass_drawing.c | 12 | ||||
-rw-r--r-- | libass/ass_parse.c | 256 | ||||
-rw-r--r-- | libass/ass_render.c | 39 | ||||
-rw-r--r-- | libass/ass_render.h | 3 | ||||
-rw-r--r-- | libass/ass_shaper.c | 44 | ||||
-rw-r--r-- | libass/ass_utils.c | 6 |
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); |