Mercurial > mplayer.hg
changeset 36363:c3aaaf17c721
Update libass to latest git version.
author | reimar |
---|---|
date | Tue, 24 Sep 2013 20:50:02 +0000 |
parents | 99708d402208 |
children | 4f8cf378dba4 |
files | libass/ass.c libass/ass.h libass/ass_bitmap.c libass/ass_bitmap.h libass/ass_font.c libass/ass_fontconfig.c libass/ass_parse.c libass/ass_render.c libass/ass_render.h libass/ass_render_api.c libass/ass_shaper.c libass/ass_types.h libass/ass_utils.c libass/ass_utils.h |
diffstat | 14 files changed, 434 insertions(+), 70 deletions(-) [+] |
line wrap: on
line diff
--- a/libass/ass.c Sun Sep 22 09:03:30 2013 +0000 +++ b/libass/ass.c Tue Sep 24 20:50:02 2013 +0000 @@ -387,6 +387,8 @@ track->ScaledBorderAndShadow = parse_bool(token); else if (!strcasecmp(*fs, "Kerning")) track->Kerning = parse_bool(token); + else if (!strcasecmp(*fs, "YCbCr Matrix")) + track->YCbCrMatrix = parse_ycbcr_matrix(token); dt = strrchr(*fs, '.'); if (dt) { @@ -424,6 +426,7 @@ FPVAL(ScaleY) FPVAL(Outline) FPVAL(Shadow) + FPVAL(Blur) } } } @@ -514,7 +517,7 @@ INTVAL(Underline) INTVAL(StrikeOut) FPVAL(Spacing) - INTVAL(Angle) + FPVAL(Angle) INTVAL(BorderStyle) INTVAL(Alignment) if (track->track_type == TRACK_TYPE_ASS) @@ -573,6 +576,8 @@ track->ScaledBorderAndShadow = parse_bool(str + 22); } else if (!strncmp(str, "Kerning:", 8)) { track->Kerning = parse_bool(str + 8); + } else if (!strncmp(str, "YCbCr Matrix:", 13)) { + track->YCbCrMatrix = parse_ycbcr_matrix(str + 13); } else if (!strncmp(str, "Language:", 9)) { char *p = str + 9; while (*p && isspace(*p)) p++; @@ -587,10 +592,10 @@ { track->parser_priv->state = PST_EVENTS; if (track->track_type == TRACK_TYPE_SSA) - track->event_format = strdup("Format: Marked, Start, End, Style, " + track->event_format = strdup("Marked, Start, End, Style, " "Name, MarginL, MarginR, MarginV, Effect, Text"); else - track->event_format = strdup("Format: Layer, Start, End, Style, " + track->event_format = strdup("Layer, Start, End, Style, " "Actor, MarginL, MarginR, MarginV, Effect, Text"); ass_msg(track->library, MSGL_V, "No event format found, using fallback");
--- a/libass/ass.h Sun Sep 22 09:03:30 2013 +0000 +++ b/libass/ass.h Tue Sep 24 20:50:02 2013 +0000 @@ -23,7 +23,7 @@ #include <stdarg.h> #include "ass_types.h" -#define LIBASS_VERSION 0x01010000 +#define LIBASS_VERSION 0x01020000 /* * A linked list of images produced by an ass renderer. @@ -44,6 +44,13 @@ int dst_x, dst_y; // Bitmap placement inside the video frame struct ass_image *next; // Next image, or NULL + + enum { + IMAGE_TYPE_CHARACTER, + IMAGE_TYPE_OUTLINE, + IMAGE_TYPE_SHADOW + } type; + } ASS_Image; /* @@ -153,6 +160,12 @@ /** * \brief Set the frame size in pixels, including margins. + * The renderer will never return images that are outside of the frame area. + * The value set with this function can influence the pixel aspect ratio used + * for rendering. If the frame size doesn't equal to the video size, you may + * have to use ass_set_pixel_aspect(). + * @see ass_set_pixel_aspect() + * @see ass_set_margins() * \param priv renderer handle * \param w width * \param h height @@ -160,6 +173,19 @@ void ass_set_frame_size(ASS_Renderer *priv, int w, int h); /** + * \brief Set the source image size in pixels. + * This is used to calculate the source aspect ratio and the blur scale. + * The source image size can be reset to default by setting w and h to 0. + * The value set with this function can influence the pixel aspect ratio used + * for rendering. + * @see ass_set_pixel_aspect() + * \param priv renderer handle + * \param w width + * \param h height + */ +void ass_set_storage_size(ASS_Renderer *priv, int w, int h); + +/** * \brief Set shaping level. This is merely a hint, the renderer will use * whatever is available if the request cannot be fulfilled. * \param level shaping level @@ -168,7 +194,26 @@ /** * \brief Set frame margins. These values may be negative if pan-and-scan - * is used. + * is used. The margins are in pixels. Each value specifies the distance from + * the video rectangle to the renderer frame. If a given margin value is + * positive, there will be free space between renderer frame and video area. + * If a given margin value is negative, the frame is inside the video, i.e. + * the video has been cropped. + * + * The renderer will try to keep subtitles inside the frame area. If possible, + * text is layout so that it is inside the cropped area. Subtitle events + * that can't be moved are cropped against the frame area. + * + * ass_set_use_margins() can be used to allow libass to render subtitles into + * the empty areas if margins are positive, i.e. the video area is smaller than + * the frame. (Traditionally, this has been used to show subtitles in + * the bottom "black bar" between video bottom screen border when playing 16:9 + * video on a 4:3 screen.) + * + * When using this function, it is recommended to calculate and set your own + * aspect ratio with ass_set_pixel_aspect(), as the defaults won't make any + * sense. + * @see ass_set_pixel_aspect() * \param priv renderer handle * \param t top margin * \param b bottom margin @@ -185,7 +230,29 @@ void ass_set_use_margins(ASS_Renderer *priv, int use); /** + * \brief Set pixel aspect ratio correction. + * This is the ratio of pixel width to pixel height. + * + * Generally, this is (s_w / s_h) / (d_w / d_h), where s_w and s_h is the + * video storage size, and d_w and d_h is the video display size. (Display + * and storage size can be different for anamorphic video, such as DVDs.) + * + * If the pixel aspect ratio is 0, or if the aspect ratio has never been set + * by calling this function, libass will calculate a default pixel aspect ratio + * out of values set with ass_set_frame_size() and ass_set_storage_size(). Note + * that this is useful only if the frame size corresponds to the video display + * size. Keep in mind that the margins set with ass_set_margins() are ignored + * for aspect ratio calculations as well. + * If the storage size has not been set, a pixel aspect ratio of 1 is assumed. + * \param priv renderer handle + * \param par pixel aspect ratio (1.0 means square pixels, 0 means default) + */ +void ass_set_pixel_aspect(ASS_Renderer *priv, double par); + +/** * \brief Set aspect ratio parameters. + * This calls ass_set_pixel_aspect(priv, dar / sar). + * @deprecated New code should use ass_set_pixel_aspect(). * \param priv renderer handle * \param dar display aspect ratio (DAR), prescaled for output PAR * \param sar storage aspect ratio (SAR)
--- a/libass/ass_bitmap.c Sun Sep 22 09:03:30 2013 +0000 +++ b/libass/ass_bitmap.c Tue Sep 24 20:50:02 2013 +0000 @@ -456,7 +456,7 @@ FT_Library ftlib, FT_Outline *outline, FT_Outline *border, Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s, int be, double blur_radius, FT_Vector shadow_offset, - int border_style) + int border_style, int border_visible) { blur_radius *= 2; int bbord = be > 0 ? sqrt(2 * be) : 0; @@ -485,7 +485,7 @@ while (be--) { if (*bm_o) be_blur(*bm_o); - else + if (!*bm_o || border_style == 3) be_blur(*bm_g); } @@ -493,7 +493,7 @@ if (blur_radius > 0.0) { if (*bm_o) resize_tmp(priv_blur, (*bm_o)->w, (*bm_o)->h); - else + if (!*bm_o || border_style == 3) resize_tmp(priv_blur, (*bm_g)->w, (*bm_g)->h); generate_tables(priv_blur, blur_radius); if (*bm_o) @@ -501,7 +501,7 @@ (*bm_o)->w, (*bm_o)->h, (*bm_o)->stride, (int *) priv_blur->gt2, priv_blur->g_r, priv_blur->g_w); - else + if (!*bm_o || border_style == 3) ass_gauss_blur((*bm_g)->buffer, priv_blur->tmp, (*bm_g)->w, (*bm_g)->h, (*bm_g)->stride, (int *) priv_blur->gt2, priv_blur->g_r, @@ -512,8 +512,11 @@ if (*bm_o && border_style != 3) { *bm_s = copy_bitmap(*bm_o); fix_outline(*bm_g, *bm_o); + } else if (*bm_o && border_visible) { + *bm_s = copy_bitmap(*bm_o); } else if (*bm_o) { - *bm_s = copy_bitmap(*bm_o); + *bm_s = *bm_o; + *bm_o = 0; } else *bm_s = copy_bitmap(*bm_g);
--- a/libass/ass_bitmap.h Sun Sep 22 09:03:30 2013 +0000 +++ b/libass/ass_bitmap.h Tue Sep 24 20:50:02 2013 +0000 @@ -46,12 +46,13 @@ * \param bm_o out: pointer to the bitmap of outline (border) glyph is returned here * \param bm_g out: pointer to the bitmap of glyph shadow is returned here * \param be 1 = produces blurred bitmaps, 0 = normal bitmaps + * \param border_visible whether border is visible if border_style is 3 */ int outline_to_bitmap3(ASS_Library *library, ASS_SynthPriv *priv_blur, FT_Library ftlib, FT_Outline *outline, FT_Outline *border, Bitmap **bm_g, Bitmap **bm_o, Bitmap **bm_s, int be, double blur_radius, FT_Vector shadow_offset, - int border_style); + int border_style, int border_visible); void ass_free_bitmap(Bitmap *bm);
--- a/libass/ass_font.c Sun Sep 22 09:03:30 2013 +0000 +++ b/libass/ass_font.c Tue Sep 24 20:50:02 2013 +0000 @@ -711,12 +711,12 @@ /* "inside" contour but we can't find anything it could be * inside of - assume the font is buggy and it should be * an "outside" contour, and reverse it */ - for (j = 0; j < (end + 1 - start) / 2; j++) { - FT_Vector temp = outline->points[start + j]; - char temp2 = outline->tags[start + j]; - outline->points[start + j] = outline->points[end - j]; + for (j = 0; j < (end - start) / 2; j++) { + FT_Vector temp = outline->points[start + 1 + j]; + char temp2 = outline->tags[start + 1 + j]; + outline->points[start + 1 + j] = outline->points[end - j]; outline->points[end - j] = temp; - outline->tags[start + j] = outline->tags[end - j]; + outline->tags[start + 1 + j] = outline->tags[end - j]; outline->tags[end - j] = temp2; } dir ^= 1;
--- a/libass/ass_fontconfig.c Sun Sep 22 09:03:30 2013 +0000 +++ b/libass/ass_fontconfig.c Tue Sep 24 20:50:02 2013 +0000 @@ -179,6 +179,14 @@ rc = FcConfigSubstitute(priv->config, pat, FcMatchPattern); if (!rc) goto error; + /* Fontconfig defaults include a language setting, which it sets based on + * some environment variables or defaults to "en". Unset this as we don't + * know the real language, and because some some attached fonts lack + * non-ascii characters included in fontconfig's list of characters + * required for English support and therefore don't match the lang=en + * criterion. + */ + FcPatternDel(pat, "lang"); fsorted = FcFontSort(priv->config, pat, FcTrue, NULL, &result); ffullname = match_fullname(library, priv, family, bold, italic);
--- a/libass/ass_parse.c Sun Sep 22 09:03:30 2013 +0000 +++ b/libass/ass_parse.c Tue Sep 24 20:50:02 2013 +0000 @@ -311,7 +311,7 @@ int x0, y0, x1, y1; int res = 1; char *start = p; - skipopt('('); + skip('('); res &= mystrtoi(&p, &x0); skipopt(','); res &= mystrtoi(&p, &y0); @@ -418,6 +418,7 @@ mystrtod(&p, &x2); skip(','); mystrtod(&p, &y2); + t1 = t2 = 0; if (*p == ',') { skip(','); mystrtoll(&p, &t1); @@ -427,13 +428,20 @@ "movement6: (%f, %f) -> (%f, %f), (%" PRId64 " .. %" PRId64 ")\n", x1, y1, x2, y2, (int64_t) t1, (int64_t) t2); - } else { + // VSFilter + if (t1 > t2) { + double tmp = t2; + t2 = t1; + t1 = tmp; + } + } + if (t1 <= 0 && t2 <= 0) { t1 = 0; t2 = render_priv->state.event->Duration; ass_msg(render_priv->library, MSGL_DBG2, "movement: (%f, %f) -> (%f, %f)", x1, y1, x2, y2); } - skip(')'); + skipopt(')'); delta_t = t2 - t1; t = render_priv->time - render_priv->state.event->Start; if (t < t1) @@ -542,7 +550,7 @@ mystrtod(&p, &v1); skip(','); mystrtod(&p, &v2); - skip(')'); + skipopt(')'); ass_msg(render_priv->library, MSGL_DBG2, "pos(%f, %f)", v1, v2); if (render_priv->state.evt_type == EVENT_POSITIONED) { ass_msg(render_priv->library, MSGL_V, "Subtitle has a new \\pos " @@ -586,7 +594,7 @@ skip(','); mystrtoll(&p, &t4); } - skip(')'); + skipopt(')'); if ((render_priv->state.parsed_tags & PARSED_FADE) == 0) { render_priv->state.fade = interpolate_alpha(render_priv->time - @@ -600,7 +608,7 @@ mystrtoi(&p, &v1); skip(','); mystrtoi(&p, &v2); - skip(')'); + skipopt(')'); ass_msg(render_priv->library, MSGL_DBG2, "org(%d, %d)", v1, v2); if (!render_priv->state.have_origin) { render_priv->state.org_x = v1; @@ -617,9 +625,8 @@ double k; skip('('); for (cnt = 0; cnt < 3; ++cnt) { - if (*p == '\\') + if (!mystrtod(&p, &v[cnt])) break; - mystrtod(&p, &v[cnt]); skip(','); } if (cnt == 3) { @@ -654,15 +661,15 @@ assert(delta_t != 0.); k = pow(((double) (t - t1)) / delta_t, v3); } - while (*p == '\\') + while (*p != ')' && *p != '}' && *p != '\0') p = parse_tag(render_priv, p, k); // maybe k*pwr ? no, specs forbid nested \t's skip_to(')'); // in case there is some unknown tag or a comment - skip(')'); + skipopt(')'); } else if (mystrcmp(&p, "clip")) { char *start = p; int x0, y0, x1, y1; int res = 1; - skipopt('('); + skip('('); res &= mystrtoi(&p, &x0); skipopt(','); res &= mystrtoi(&p, &y0);
--- a/libass/ass_render.c Sun Sep 22 09:03:30 2013 +0000 +++ b/libass/ass_render.c Tue Sep 24 20:50:02 2013 +0000 @@ -240,7 +240,7 @@ static ASS_Image **render_glyph_i(ASS_Renderer *render_priv, Bitmap *bm, int dst_x, int dst_y, uint32_t color, uint32_t color2, int brk, - ASS_Image **tail) + ASS_Image **tail, unsigned int type) { int i, j, x0, y0, x1, y1, cx0, cy0, cx1, cy1, sx, sy, zx, zy; Rect r[4]; @@ -308,6 +308,7 @@ lbrk - r[j].x0, r[j].y1 - r[j].y0, bm->stride, dst_x + r[j].x0, dst_y + r[j].y0, color); if (!img) break; + img->type = type; *tail = img; tail = &img->next; } @@ -317,6 +318,7 @@ r[j].x1 - lbrk, r[j].y1 - r[j].y0, bm->stride, dst_x + lbrk, dst_y + r[j].y0, color2); if (!img) break; + img->type = type; *tail = img; tail = &img->next; } @@ -339,12 +341,12 @@ */ static ASS_Image ** render_glyph(ASS_Renderer *render_priv, Bitmap *bm, int dst_x, int dst_y, - uint32_t color, uint32_t color2, int brk, ASS_Image **tail) + uint32_t color, uint32_t color2, int brk, ASS_Image **tail, unsigned int type) { // Inverse clipping in use? if (render_priv->state.clip_mode) return render_glyph_i(render_priv, bm, dst_x, dst_y, color, color2, - brk, tail); + brk, tail, type); // brk is relative to dst_x // color = color left of brk @@ -399,6 +401,7 @@ brk - b_x0, b_y1 - b_y0, bm->stride, dst_x + b_x0, dst_y + b_y0, color); if (!img) return tail; + img->type = type; *tail = img; tail = &img->next; } @@ -409,6 +412,7 @@ b_x1 - brk, b_y1 - b_y0, bm->stride, dst_x + brk, dst_y + b_y0, color2); if (!img) return tail; + img->type = type; *tail = img; tail = &img->next; } @@ -718,7 +722,7 @@ here_tail = tail; tail = render_glyph(render_priv, bm, pen_x, pen_y, info->c[3], 0, - 1000000, tail); + 1000000, tail, IMAGE_TYPE_SHADOW); if (last_tail && tail != here_tail && ((info->c[3] & 0xff) > 0)) render_overlap(render_priv, last_tail, here_tail); @@ -752,7 +756,7 @@ here_tail = tail; tail = render_glyph(render_priv, bm, pen_x, pen_y, info->c[2], - 0, 1000000, tail); + 0, 1000000, tail, IMAGE_TYPE_OUTLINE); if (last_tail && tail != here_tail && ((info->c[2] & 0xff) > 0)) render_overlap(render_priv, last_tail, here_tail); @@ -783,19 +787,19 @@ if (info->effect_timing > (info->bbox.xMax >> 6)) tail = render_glyph(render_priv, bm, pen_x, pen_y, - info->c[0], 0, 1000000, tail); + info->c[0], 0, 1000000, tail, IMAGE_TYPE_CHARACTER); else tail = render_glyph(render_priv, bm, pen_x, pen_y, - info->c[1], 0, 1000000, tail); + info->c[1], 0, 1000000, tail, IMAGE_TYPE_CHARACTER); } else if (info->effect_type == EF_KARAOKE_KF) { tail = render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], - info->c[1], info->effect_timing, tail); + info->c[1], info->effect_timing, tail, IMAGE_TYPE_CHARACTER); } else tail = render_glyph(render_priv, bm, pen_x, pen_y, info->c[0], - 0, 1000000, tail); + 0, 1000000, tail, IMAGE_TYPE_CHARACTER); info = info->next; } } @@ -866,7 +870,7 @@ render_priv->state.scale_y = style->ScaleY; render_priv->state.hspacing = style->Spacing; render_priv->state.be = 0; - render_priv->state.blur = 0.0; + render_priv->state.blur = style->Blur; render_priv->state.shadow_x = style->Shadow; render_priv->state.shadow_y = style->Shadow; render_priv->state.frx = render_priv->state.fry = 0.; @@ -927,13 +931,14 @@ * Replace the outline of a glyph by a contour which makes up a simple * opaque rectangle. */ -static void draw_opaque_box(ASS_Renderer *render_priv, int asc, int desc, - FT_Outline *ol, FT_Vector advance, int sx, int sy) +static void draw_opaque_box(ASS_Renderer *render_priv, GlyphInfo *info, + int asc, int desc, FT_Outline *ol, + FT_Vector advance, int sx, int sy) { int i; int adv = advance.x; - double scale_y = render_priv->state.scale_y; - double scale_x = render_priv->state.scale_x; + double scale_y = info->scale_y; + double scale_x = info->scale_x; // to avoid gaps sx = FFMAX(64, sx); @@ -941,8 +946,7 @@ // Emulate the WTFish behavior of VSFilter, i.e. double-scale // the sizes of the opaque box. - adv += double_to_d6(render_priv->state.hspacing * render_priv->font_scale - * scale_x); + adv += double_to_d6(info->hspacing * render_priv->font_scale * scale_x); adv *= scale_x; sx *= scale_x; sy *= scale_y; @@ -1127,8 +1131,7 @@ FT_Outline_Get_CBox(v.outline, &v.bbox_scaled); - if (info->border_style == 3 && - (info->border_x > 0 || info->border_y > 0)) { + if (info->border_style == 3) { FT_Vector advance; v.border = calloc(1, sizeof(FT_Outline)); @@ -1138,7 +1141,7 @@ else advance = info->advance; - draw_opaque_box(priv, v.asc, v.desc, v.border, advance, + draw_opaque_box(priv, info, v.asc, v.desc, v.border, advance, double_to_d6(info->border_x * priv->border_scale), double_to_d6(info->border_y * priv->border_scale)); @@ -1311,9 +1314,10 @@ outline, border, &hash_val.bm, &hash_val.bm_o, &hash_val.bm_s, info->be, - info->blur * render_priv->border_scale, + info->blur * render_priv->blur_scale, key->shadow_offset, - info->border_style); + info->border_style, + info->border_x || info->border_y); if (error) info->symbol = 0; @@ -2244,21 +2248,37 @@ render_priv->font_scale = settings_priv->font_size_coeff * render_priv->orig_height / render_priv->track->PlayResY; + if (render_priv->storage_height) + render_priv->blur_scale = ((double) render_priv->orig_height) / + render_priv->storage_height; + else + render_priv->blur_scale = 1.; if (render_priv->track->ScaledBorderAndShadow) render_priv->border_scale = ((double) render_priv->orig_height) / render_priv->track->PlayResY; else - render_priv->border_scale = 1.; + render_priv->border_scale = render_priv->blur_scale; + render_priv->border_scale *= settings_priv->font_size_coeff; ass_shaper_set_kerning(render_priv->shaper, track->Kerning); - if (track->Language) - ass_shaper_set_language(render_priv->shaper, track->Language); + ass_shaper_set_language(render_priv->shaper, track->Language); ass_shaper_set_level(render_priv->shaper, render_priv->settings.shaper); // PAR correction - render_priv->font_scale_x = render_priv->settings.aspect / - render_priv->settings.storage_aspect; + double par = render_priv->settings.par; + if (par == 0.) { + if (settings_priv->frame_width && settings_priv->frame_height && + settings_priv->storage_width && settings_priv->storage_height) { + double dar = ((double) settings_priv->frame_width) / + settings_priv->frame_height; + double sar = ((double) settings_priv->storage_width) / + settings_priv->storage_height; + par = sar / dar; + } else + par = 1.0; + } + render_priv->font_scale_x = par; render_priv->prev_images_root = render_priv->images_root; render_priv->images_root = 0;
--- a/libass/ass_render.h Sun Sep 22 09:03:30 2013 +0000 +++ b/libass/ass_render.h Tue Sep 24 20:50:02 2013 +0000 @@ -26,6 +26,9 @@ #include FT_STROKER_H #include FT_GLYPH_H #include FT_SYNTHESIS_H +#ifdef CONFIG_HARFBUZZ +#include "hb.h" +#endif // XXX: fix the inclusion mess so we can avoid doing this here typedef struct ass_shaper ASS_Shaper; @@ -65,6 +68,8 @@ typedef struct { int frame_width; int frame_height; + int storage_width; // width of the source image + int storage_height; // height of the source image double font_size_coeff; // font size multiplier double line_spacing; // additional line spacing (in frame pixels) double line_position; // vertical position for subtitles, 0-100 (0 = no change) @@ -74,8 +79,7 @@ int right_margin; int use_margins; // 0 - place all subtitles inside original frame // 1 - use margins for placing toptitles and subtitles - double aspect; // frame aspect ratio, d_width / d_height. - double storage_aspect; // pixel ratio of the source image + double par; // user defined pixel aspect ratio (0 = unset) ASS_Hinting hinting; ASS_ShapingLevel shaper; @@ -107,6 +111,11 @@ ASS_Font *font; int face_index; int glyph_index; +#ifdef CONFIG_HARFBUZZ + hb_script_t script; +#else + int script; +#endif double font_size; ASS_Drawing *drawing; FT_Outline *outline; @@ -264,11 +273,14 @@ int orig_width; // frame width ( = screen width - margins ) int orig_height_nocrop; // frame height ( = screen height - margins + cropheight) int orig_width_nocrop; // frame width ( = screen width - margins + cropwidth) + int storage_height; // video height before any rescaling + int storage_width; // video width before any rescaling ASS_Track *track; long long time; // frame's timestamp, ms double font_scale; double font_scale_x; // x scale applied to all glyphs to preserve text aspect ratio double border_scale; + double blur_scale; RenderContext state; TextInfo text_info;
--- a/libass/ass_render_api.c Sun Sep 22 09:03:30 2013 +0000 +++ b/libass/ass_render_api.c Tue Sep 24 20:50:02 2013 +0000 @@ -43,6 +43,13 @@ priv->orig_height_nocrop = settings->frame_height - FFMAX(settings->top_margin, 0) - FFMAX(settings->bottom_margin, 0); + if (settings->storage_height) { + priv->storage_width = settings->storage_width; + priv->storage_height = settings->storage_height; + } else { + priv->storage_width = priv->orig_width; + priv->storage_height = priv->orig_height; + } } void ass_set_frame_size(ASS_Renderer *priv, int w, int h) @@ -50,10 +57,16 @@ if (priv->settings.frame_width != w || priv->settings.frame_height != h) { priv->settings.frame_width = w; priv->settings.frame_height = h; - if (priv->settings.aspect == 0.) { - priv->settings.aspect = ((double) w) / h; - priv->settings.storage_aspect = ((double) w) / h; - } + ass_reconfigure(priv); + } +} + +void ass_set_storage_size(ASS_Renderer *priv, int w, int h) +{ + if (priv->settings.storage_width != w || + priv->settings.storage_height != h) { + priv->settings.storage_width = w; + priv->settings.storage_height = h; ass_reconfigure(priv); } } @@ -88,9 +101,13 @@ void ass_set_aspect_ratio(ASS_Renderer *priv, double dar, double sar) { - if (priv->settings.aspect != dar || priv->settings.storage_aspect != sar) { - priv->settings.aspect = dar; - priv->settings.storage_aspect = sar; + ass_set_pixel_aspect(priv, dar / sar); +} + +void ass_set_pixel_aspect(ASS_Renderer *priv, double par) +{ + if (priv->settings.par != par) { + priv->settings.par = par; ass_reconfigure(priv); } }
--- a/libass/ass_shaper.c Sun Sep 22 09:03:30 2013 +0000 +++ b/libass/ass_shaper.c Tue Sep 24 20:50:02 2013 +0000 @@ -213,6 +213,12 @@ return NULL; memcpy(&new_val.metrics, &face->glyph->metrics, sizeof(FT_Glyph_Metrics)); + + // 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) + new_val.metrics.horiAdvance = new_val.metrics.vertAdvance; + val = ass_cache_put(metrics->metrics_cache, &metrics->hash_key, &new_val); } @@ -244,9 +250,6 @@ if (!metrics) return 0; - if (metrics_priv->vertical && glyph > VERTICAL_LOWER_BOUND) - return metrics->metrics.vertAdvance; - return metrics->metrics.horiAdvance; } @@ -419,6 +422,107 @@ } /** + * \brief Map script to default language. + * + * This maps a script to a language, if a script has a representative + * language it is typically used with. Otherwise, the invalid language + * is returned. + * + * The mapping is similar to Pango's pango-language.c. + * + * \param script script tag + * \return language tag + */ +static hb_language_t script_to_language(hb_script_t script) +{ + switch (script) { + // Unicode 1.1 + case HB_SCRIPT_ARABIC: return hb_language_from_string("ar", -1); break; + case HB_SCRIPT_ARMENIAN: return hb_language_from_string("hy", -1); break; + case HB_SCRIPT_BENGALI: return hb_language_from_string("bn", -1); break; + case HB_SCRIPT_CANADIAN_ABORIGINAL: return hb_language_from_string("iu", -1); break; + case HB_SCRIPT_CHEROKEE: return hb_language_from_string("chr", -1); break; + case HB_SCRIPT_COPTIC: return hb_language_from_string("cop", -1); break; + case HB_SCRIPT_CYRILLIC: return hb_language_from_string("ru", -1); break; + case HB_SCRIPT_DEVANAGARI: return hb_language_from_string("hi", -1); break; + case HB_SCRIPT_GEORGIAN: return hb_language_from_string("ka", -1); break; + case HB_SCRIPT_GREEK: return hb_language_from_string("el", -1); break; + case HB_SCRIPT_GUJARATI: return hb_language_from_string("gu", -1); break; + case HB_SCRIPT_GURMUKHI: return hb_language_from_string("pa", -1); break; + case HB_SCRIPT_HANGUL: return hb_language_from_string("ko", -1); break; + case HB_SCRIPT_HEBREW: return hb_language_from_string("he", -1); break; + case HB_SCRIPT_HIRAGANA: return hb_language_from_string("ja", -1); break; + case HB_SCRIPT_KANNADA: return hb_language_from_string("kn", -1); break; + case HB_SCRIPT_KATAKANA: return hb_language_from_string("ja", -1); break; + case HB_SCRIPT_LAO: return hb_language_from_string("lo", -1); break; + case HB_SCRIPT_LATIN: return hb_language_from_string("en", -1); break; + case HB_SCRIPT_MALAYALAM: return hb_language_from_string("ml", -1); break; + case HB_SCRIPT_MONGOLIAN: return hb_language_from_string("mn", -1); break; + case HB_SCRIPT_ORIYA: return hb_language_from_string("or", -1); break; + case HB_SCRIPT_SYRIAC: return hb_language_from_string("syr", -1); break; + case HB_SCRIPT_TAMIL: return hb_language_from_string("ta", -1); break; + case HB_SCRIPT_TELUGU: return hb_language_from_string("te", -1); break; + case HB_SCRIPT_THAI: return hb_language_from_string("th", -1); break; + + // Unicode 2.0 + case HB_SCRIPT_TIBETAN: return hb_language_from_string("bo", -1); break; + + // Unicode 3.0 + case HB_SCRIPT_ETHIOPIC: return hb_language_from_string("am", -1); break; + case HB_SCRIPT_KHMER: return hb_language_from_string("km", -1); break; + case HB_SCRIPT_MYANMAR: return hb_language_from_string("my", -1); break; + case HB_SCRIPT_SINHALA: return hb_language_from_string("si", -1); break; + case HB_SCRIPT_THAANA: return hb_language_from_string("dv", -1); break; + + // Unicode 3.2 + case HB_SCRIPT_BUHID: return hb_language_from_string("bku", -1); break; + case HB_SCRIPT_HANUNOO: return hb_language_from_string("hnn", -1); break; + case HB_SCRIPT_TAGALOG: return hb_language_from_string("tl", -1); break; + case HB_SCRIPT_TAGBANWA: return hb_language_from_string("tbw", -1); break; + + // Unicode 4.0 + case HB_SCRIPT_UGARITIC: return hb_language_from_string("uga", -1); break; + + // Unicode 4.1 + case HB_SCRIPT_BUGINESE: return hb_language_from_string("bug", -1); break; + case HB_SCRIPT_OLD_PERSIAN: return hb_language_from_string("peo", -1); break; + case HB_SCRIPT_SYLOTI_NAGRI: return hb_language_from_string("syl", -1); break; + + // Unicode 5.0 + case HB_SCRIPT_NKO: return hb_language_from_string("nko", -1); break; + + // no representative language exists + default: return HB_LANGUAGE_INVALID; break; + } +} + +/** + * \brief Determine language to be used for shaping a run. + * + * \param shaper shaper instance + * \param script script tag associated with run + * \return language tag + */ +static hb_language_t +hb_shaper_get_run_language(ASS_Shaper *shaper, hb_script_t script) +{ + hb_language_t lang; + + // override set, use it + if (shaper->language != HB_LANGUAGE_INVALID) + return shaper->language; + + // get default language for given script + lang = script_to_language(script); + + // no dice, use system default + if (lang == HB_LANGUAGE_INVALID) + lang = hb_language_get_default(); + + return lang; +} + +/** * \brief Shape event text with HarfBuzz. Full OpenType shaping. * \param glyphs glyph clusters * \param len number of clusters @@ -440,6 +544,7 @@ int k = i; int level = glyphs[i].shape_run_id; int direction = shaper->emblevels[k] % 2; + hb_script_t script = glyphs[i].script; while (i < (len - 1) && level == glyphs[i+1].shape_run_id) i++; runs[run].offset = k; @@ -450,7 +555,9 @@ hb_buffer_pre_allocate(runs[run].buf, i - k + 1); hb_buffer_set_direction(runs[run].buf, direction ? HB_DIRECTION_RTL : HB_DIRECTION_LTR); - hb_buffer_set_language(runs[run].buf, shaper->language); + hb_buffer_set_language(runs[run].buf, + hb_shaper_get_run_language(shaper, script)); + hb_buffer_set_script(runs[run].buf, script); hb_buffer_add_utf32(runs[run].buf, shaper->event_text + k, i - k + 1, 0, i - k + 1); hb_shape(runs[run].font, runs[run].buf, shaper->features, @@ -503,6 +610,56 @@ } } + +/** + * \brief Determine script property of all characters. Characters of script + * common and inherited get their script from their context. + * + */ +void ass_shaper_determine_script(ASS_Shaper *shaper, GlyphInfo *glyphs, + size_t len) +{ + int i; + int backwards_scan = 0; + hb_unicode_funcs_t *ufuncs = hb_unicode_funcs_get_default(); + hb_script_t last_script = HB_SCRIPT_UNKNOWN; + + // determine script (forward scan) + for (i = 0; i < len; i++) { + GlyphInfo *info = glyphs + i; + info->script = hb_unicode_script(ufuncs, info->symbol); + + // common/inherit codepoints inherit script from context + if (info->script == HB_SCRIPT_COMMON || + info->script == HB_SCRIPT_INHERITED) { + // unknown is not a valid context + if (last_script != HB_SCRIPT_UNKNOWN) + info->script = last_script; + else + // do a backwards scan to check if next codepoint + // contains a valid script for context + backwards_scan = 1; + } else { + last_script = info->script; + } + } + + // determine script (backwards scan, if needed) + last_script = HB_SCRIPT_UNKNOWN; + for (i = len - 1; i >= 0 && backwards_scan; i--) { + GlyphInfo *info = glyphs + i; + + // common/inherit codepoints inherit script from context + if (info->script == HB_SCRIPT_COMMON || + info->script == HB_SCRIPT_INHERITED) { + // unknown script is not a valid context + if (last_script != HB_SCRIPT_UNKNOWN) + info->script = last_script; + } else { + last_script = info->script; + } + } +} #endif #ifdef CONFIG_FRIBIDI @@ -555,6 +712,11 @@ int i; int shape_run = 0; +#ifdef CONFIG_HARFBUZZ + ass_shaper_determine_script(shaper, glyphs, len); +#endif + + // find appropriate fonts for the shape runs for (i = 0; i < len; i++) { GlyphInfo *last = glyphs + i - 1; GlyphInfo *info = glyphs + i; @@ -567,11 +729,11 @@ // shape runs share the same font face and size if (i > 0 && (last->font != info->font || last->font_size != info->font_size || - last->face_index != info->face_index)) + last->face_index != info->face_index || + last->script != info->script)) shape_run++; info->shape_run_id = shape_run; } - } /** @@ -591,7 +753,14 @@ void ass_shaper_set_language(ASS_Shaper *shaper, const char *code) { #ifdef CONFIG_HARFBUZZ - shaper->language = hb_language_from_string(code, -1); + hb_language_t lang; + + if (code) + lang = hb_language_from_string(code, -1); + else + lang = HB_LANGUAGE_INVALID; + + shaper->language = lang; #endif }
--- a/libass/ass_types.h Sun Sep 22 09:03:30 2013 +0000 +++ b/libass/ass_types.h Tue Sep 24 20:50:02 2013 +0000 @@ -50,7 +50,7 @@ double ScaleX; double ScaleY; double Spacing; - int Angle; + double Angle; int BorderStyle; double Outline; double Shadow; @@ -60,6 +60,7 @@ int MarginV; int Encoding; int treat_fontname_as_pattern; + double Blur; } ASS_Style; /* @@ -113,6 +114,19 @@ int ScaledBorderAndShadow; int Kerning; char *Language; + enum { + YCBCR_DEFAULT = 0, // TV.601 on YCbCr video, None on RGB video + YCBCR_UNKNOWN, + YCBCR_NONE, // untouched RGB values + YCBCR_BT601_TV, + YCBCR_BT601_PC, + YCBCR_BT709_TV, + YCBCR_BT709_PC, + YCBCR_SMPTE240M_TV, + YCBCR_SMPTE240M_PC, + YCBCR_FCC_TV, + YCBCR_FCC_PC + } YCbCrMatrix; int default_style; // index of default style char *name; // file name in case of external subs, 0 for streams
--- a/libass/ass_utils.c Sun Sep 22 09:03:30 2013 +0000 +++ b/libass/ass_utils.c Tue Sep 24 20:50:02 2013 +0000 @@ -122,6 +122,46 @@ return 0; } +int parse_ycbcr_matrix(char *str) +{ + while (*str == ' ' || *str == '\t') + str++; + if (*str == '\0') + return YCBCR_DEFAULT; + + char *end = str + strlen(str); + while (end[-1] == ' ' || end[-1] == '\t') + end--; + + // Trim a local copy of the input that we know is safe to + // modify. The buffer is larger than any valid string + NUL, + // so we can simply chop off the rest of the input. + char buffer[16]; + size_t n = FFMIN(end - str, sizeof buffer - 1); + strncpy(buffer, str, n); + buffer[n] = '\0'; + + if (!strcasecmp(buffer, "none")) + return YCBCR_NONE; + if (!strcasecmp(buffer, "tv.601")) + return YCBCR_BT601_TV; + if (!strcasecmp(buffer, "pc.601")) + return YCBCR_BT601_PC; + if (!strcasecmp(buffer, "tv.709")) + return YCBCR_BT709_TV; + if (!strcasecmp(buffer, "pc.709")) + return YCBCR_BT709_PC; + if (!strcasecmp(buffer, "tv.240m")) + return YCBCR_SMPTE240M_TV; + if (!strcasecmp(buffer, "pc.240m")) + return YCBCR_SMPTE240M_PC; + if (!strcasecmp(buffer, "tv.fcc")) + return YCBCR_FCC_TV; + if (!strcasecmp(buffer, "pc.fcc")) + return YCBCR_FCC_PC; + return YCBCR_UNKNOWN; +} + void ass_msg(ASS_Library *priv, int lvl, char *fmt, ...) { va_list va;
--- a/libass/ass_utils.h Sun Sep 22 09:03:30 2013 +0000 +++ b/libass/ass_utils.h Tue Sep 24 20:50:02 2013 +0000 @@ -49,6 +49,7 @@ int mystrtod(char **p, double *res); int strtocolor(ASS_Library *library, char **q, uint32_t *res, int hex); char parse_bool(char *str); +int parse_ycbcr_matrix(char *str); unsigned ass_utf8_get_char(char **str); void ass_msg(ASS_Library *priv, int lvl, char *fmt, ...); int lookup_style(ASS_Track *track, char *name);